I keep hearing about how great QML is to learn so I'm a bit baffled as to why I am still having such a hard time with getting more than a toy program written. I'm trying to save my rant about that once I've gotten an application completed and can help provide more useful feedback. In the mean time I wanted to cover a set of changes to my python code that will made the QML port a bit easier. This is regarding code to make it easy both for users to provide good bug reports and for me to figure out what went wrong.
tl;dr
Some sample programs that demonstrate the ideas in this post
Showing posts with label maemo. Show all posts
Showing posts with label maemo. Show all posts
Thursday, September 08, 2011
Saturday, August 13, 2011
A Swipe UX Idea
Over my desk at work I have a sign that says "On my deathbed I would design a better deathbed".
I've been using the N950 now for several weeks and I can't help thinking about how I would do things differently. This is not meant to criticize Nokia's work on Swipe. Swipe works well as a universal gesture. Even if there are issues with it you and I should recognize this is beta software. I've heard Nokia has already made one change to swipe since my software build and who knows what else might change before release.
I'm just curious about how else things could be done and the effect they might have on the user experience. I don't think Nokia should immediately drop everything and switch to my idea. I first would like to see what they come up with in the finished project. If I still think mine is better? Oh well. People think and act differently, especially programmers, and they are probably targeting others.
Swipe UX
I'd recommend these demos for a quick introduction into swipe. The main swipes will bring you back to the last home screen you used. When wanting to swipe to a specific screen you swipe back to the home screen you were on, pause to see which it was, and then swipe over. You can go about it faster by remembering what screen you were on. I usually think I know where I was but I tend to be wrong leading me to swipe a few more times.
My variant on Swipe UX
My idea centers on a spatial model that spans home screens, the lock screen, and applications. This could roughly be translated to virtual desktops with rules controlling which desktop different windows get launched on based on the desktop's role.
Launch an app, no matter from what screen? As it is opening the device also slides home screens over to the task switcher. Now with the app up a swipe down reveals the task switcher below. If instead you swipe up the app closes. Also you can swipe to the right to move to the launcher or left to move to the events feed.
The device goes to sleep? The lock screen is above the events feed. A swipe up or down reveals the feed below. Instead a swipe right moves to the task switcher and left for the launcher.
Open up a folder of icons? The subfolder opens up above the launcher. A swipe up or down to reveals the main launcher below. This would provide a simple UI for subfolders to be built into the launcher.. Again instead a swipe right moves to the events feed, and left for the task switcher.
Applications, home screens, and the lock screen would operate on a more consistent set of simpler rules using a spatial model for the home screens rather than most-recently-used. From where you are you would always know how to get to where you want to be with one swipe without a pause to think of which screen you are on and what direction you need to go to get to where you want or having to keep an accurate mental copy of the device state.
*Note: The images are mockups only and were hacked together from screenshots I found through Google.
I've been using the N950 now for several weeks and I can't help thinking about how I would do things differently. This is not meant to criticize Nokia's work on Swipe. Swipe works well as a universal gesture. Even if there are issues with it you and I should recognize this is beta software. I've heard Nokia has already made one change to swipe since my software build and who knows what else might change before release.
I'm just curious about how else things could be done and the effect they might have on the user experience. I don't think Nokia should immediately drop everything and switch to my idea. I first would like to see what they come up with in the finished project. If I still think mine is better? Oh well. People think and act differently, especially programmers, and they are probably targeting others.
Swipe UX
I'd recommend these demos for a quick introduction into swipe. The main swipes will bring you back to the last home screen you used. When wanting to swipe to a specific screen you swipe back to the home screen you were on, pause to see which it was, and then swipe over. You can go about it faster by remembering what screen you were on. I usually think I know where I was but I tend to be wrong leading me to swipe a few more times.
My variant on Swipe UX
My idea centers on a spatial model that spans home screens, the lock screen, and applications. This could roughly be translated to virtual desktops with rules controlling which desktop different windows get launched on based on the desktop's role.
Launch an app, no matter from what screen? As it is opening the device also slides home screens over to the task switcher. Now with the app up a swipe down reveals the task switcher below. If instead you swipe up the app closes. Also you can swipe to the right to move to the launcher or left to move to the events feed.
![]() |
Idea: Swipe up or down from the lock screen to unlock and reveal the feed |
![]() |
Idea: Swipe left on the lock screen to unlock and move to the launcher |
![]() |
Idea: Swipe Right on the lock screen to unlock and move to task switcher |
*Note: The images are mockups only and were hacked together from screenshots I found through Google.
Python Packaging for Harmattan
Right now I am taking a break from learning QML. Instead I've been
adding support for Harmattan to my existing QWidget projects. It might seem pretty pointless because of how aweful QWidget can be with the default theme. I will at least have the apps available if I need them in a bind. Also taking working apps and repackaging them rather than new untested apps provides the benefit of controlling the number of variables to debug when things go wrong.
I will be focusing on packaging for OBS seeing as a QWidget-based application isn't worth pushing through OVI.
Packaging
I had to switch my applications packaging over from my py2deb fork to the distutils-based sdist_maemo. A big reason is sdist_maemo already has Harmattan support. I also figured it was a good idea to switch to distutils as it is more "proper" python packaging and because it is pluggable for various target packaging systems. That last part will allow me to continue to support Maemo 4.1/5 and in the future support regular MeeGo.
So first I've moved all of my files to a proper distutils based layout
I then wrote my setup.py file with an sdist_maemo command configured for each of my target deb-based distributions. A nice thing to know is that for Maemo 5 the python module will be optified for you.
An annoying thing is each platform has different packaging requirements. Some examples include:
To minimize my need for using code-gen my setup.py file creates multiple instances of sdist_maemo, one for each platform.
The app icon caused me some problems. I had assumed it would operate like previous versions of Maemo and desktop Linux, that if you just said "ejpi" or "gonvert" it would look for the appropriate icon size in "/usr/share/icons/hicolor". I'm glad I found that document explaining otherwise. As an alternative some of the apps in the OVI Store just hard code an absolute path. When you do make a mistake on this, Harmattan seems to have two different default icons. The red square came up for me when "ejpi" didn't map to an icon. The green curved icon came up when I had an absolute path that pointed at nothing.
My makefile creates the needed icons, code-gens my desktop files, code-gens my setup.py files, and then runs "setup.*.py sdist_*".
Odds and Ends
I do not believe PyQt is available for Harmattan, so I had to finish adding support for PySide in my applications. I've made it easy to use both bindings by centralizing all knowledge of the differences between the two.
I've tried to make the switch a couple of times already but each time I came across new bugs that the old bugs prevented me from seeing. Luckily the PySide people are quick at resolving these issues and by 1.0.5 my applications ran smoothly.
I went ahead and made the simple switch to XDG and Harmattan-style icons though I'm not yet seeking OVI compliance. I decided to redo the icons. They weren't all that great, no where near the icon guidelines, and I wanted SVGs so I could use the Harmattan Icon Generator. Not being an expert at graphics tools, the icon generator definitely saved me a good amount of work. The main drawback is that basing the icons background gradient on the base icon's content seems to produce results without enough contrast. I ended up using the "mean" setting to force gray backgrounds.
Distributing
Now that we have all that wrapped up, time to upload onto the OBS. The Getting Started Guide has been updated and now is pretty good. I setup a harmattan-specific subproject due to my code-gen specializing the source-packages per distribution. Once everything was uploaded to my project, I logged in as root, added my sources, and away I went.
So for the fruit of my work:
Resources
Desktop File:
XDG
Icon
OBS
I will be focusing on packaging for OBS seeing as a QWidget-based application isn't worth pushing through OVI.
Packaging
I had to switch my applications packaging over from my py2deb fork to the distutils-based sdist_maemo. A big reason is sdist_maemo already has Harmattan support. I also figured it was a good idea to switch to distutils as it is more "proper" python packaging and because it is pluggable for various target packaging systems. That last part will allow me to continue to support Maemo 4.1/5 and in the future support regular MeeGo.
So first I've moved all of my files to a proper distutils based layout
- ./setup.py
- ./LauncherName
- ./package-name/*.py
- ./data/package-name.desktop
- ./data/package-name.png
I then wrote my setup.py file with an sdist_maemo command configured for each of my target deb-based distributions. A nice thing to know is that for Maemo 5 the python module will be optified for you.
An annoying thing is each platform has different packaging requirements. Some examples include:
- Different ".desktop" file locations
- Different ways of launching the app from within a desktop file.
- Everything seems different with the app launcher icon
- Different icon sizes for the Maemo-Icon-26 field
- Different .deb sections
- Different dependency names
- Aegis file support (not needed for current apps so skipped over)
To minimize my need for using code-gen my setup.py file creates multiple instances of sdist_maemo, one for each platform.
The app icon caused me some problems. I had assumed it would operate like previous versions of Maemo and desktop Linux, that if you just said "ejpi" or "gonvert" it would look for the appropriate icon size in "/usr/share/icons/hicolor". I'm glad I found that document explaining otherwise. As an alternative some of the apps in the OVI Store just hard code an absolute path. When you do make a mistake on this, Harmattan seems to have two different default icons. The red square came up for me when "ejpi" didn't map to an icon. The green curved icon came up when I had an absolute path that pointed at nothing.
My makefile creates the needed icons, code-gens my desktop files, code-gens my setup.py files, and then runs "setup.*.py sdist_*".
Odds and Ends
I do not believe PyQt is available for Harmattan, so I had to finish adding support for PySide in my applications. I've made it easy to use both bindings by centralizing all knowledge of the differences between the two.
I've tried to make the switch a couple of times already but each time I came across new bugs that the old bugs prevented me from seeing. Luckily the PySide people are quick at resolving these issues and by 1.0.5 my applications ran smoothly.
I went ahead and made the simple switch to XDG and Harmattan-style icons though I'm not yet seeking OVI compliance. I decided to redo the icons. They weren't all that great, no where near the icon guidelines, and I wanted SVGs so I could use the Harmattan Icon Generator. Not being an expert at graphics tools, the icon generator definitely saved me a good amount of work. The main drawback is that basing the icons background gradient on the base icon's content seems to produce results without enough contrast. I ended up using the "mean" setting to force gray backgrounds.
Distributing
Now that we have all that wrapped up, time to upload onto the OBS. The Getting Started Guide has been updated and now is pretty good. I setup a harmattan-specific subproject due to my code-gen specializing the source-packages per distribution. Once everything was uploaded to my project, I logged in as root, added my sources, and away I went.
So for the fruit of my work:
- ejpi and gonvert source including support for Maemo 4.1, Maemo 5, and Harmattan
- my Harmattan OBS project
- my Harmattan repository
Resources
Desktop File:
XDG
Icon
- Guidelines
- Icon Generator with Color suggestions
- Finding icons
OBS
- Getting Started with OBS (including both MeeGo and Harmattan)
- Plan for Apps
Tuesday, July 19, 2011
N950 Arrival and My Development Plans
A couple of weeks ago I was notified by Quim Gil that I was selected for the MeeGo Community Developer Device Program for a Nokia N950 devkit. Prior to this I had been quiet in Maemo-land for a little bit while I worked on an experimental rewrite of one of my applications that uses PyGame. Seeing as PyGame isn't on the N950 I hurried up in getting it to a state I could put aside and moved on to learning QML.
MeeGo Development
My development plans have changed (and frustration increased) over time as the MeeGo development story has gotten refined. This is due to both changes in the UI toolkit availability and rules for distribution of applications.
Originally when things shifted to Qt I chose QWidget because it was ready now, includes widgets, and was recommended by Nokia (you know, the people who own Qt) for the types of apps I write (which recommendation was removed on June 22, 2011). At MeeGo Conference I found out that some (most? all?) MeeGo instances are not including QWdget themes (which without a theme its basically dead). QWidget in Qt5 will be optional and will probably not be included on those same MeeGo instances that currently don't ship a theme. So there goes that work.
I am still a bit frustrated with QML in that there is not even a subset of common QML Components between platforms though some disagree on it being a problem. I've just seen really terrible QML applications which mostly stem from everyone reinventing the same bugs when writing widgets from scratch. I also find it a wonderful experience having the widgets available for the desktop such that I can write apps in a desktop environment that are targeted for small screens without the need for any pesky IDEs and emulation. When it comes to testing, I am much more efficient testing and debugging natively on the desktop and just need to spot check how it translates to my devices.
I do not look forward to packaging for generic MeeGo. I found out at MeeGo conference that dependency management isn't guaranteed to be included and one has to include in their package any dependencies outside of core MeeGo. This is fine for compiled applications, you just pull in static libs. This is not as simple for Python developers where part of the beauty is your program isn't compiled and any compiled extensions are distributed and maintained separately. I have contemplated some tools to simplify this (based on cx-freeze and Hatchet) but that is a good amount of work and focused away from what makes all of this fun.
At MeeGo conference I raised my concern for packaging Python applications during the apps.meego.com lunch time BoF. The impression I got then (no clue if things have changed) is that they will focus on MeeGo compliant apps at first and might later support others. Even with that concession this is a stark contrast to the original vision that helped bring me on board from Maemo about (what was then called) Surrounds which would serve both as an app repo and as a place for third-party dependencies. If I chose to not bundle all of my libraries then I also am not available for any of the other distribution channels (and any contests they might be running). I don't program for the reward of the contests but it isn't fun feeling excluded.
The beauty of developing for Maemo was it had very low friction in the development story thanks to Python that I was able to spend more time focusing on what was fun: designing software for end-users to enjoy. I feel that this is being lost in MeeGo.
MeeGo and Nokia
The picture can be better when focusing on a individual vendors. Vendors may keep things a bit more lax with their distribution channels. The problem is then you are customizing per vendor which requires device availability for testing and a lot more work. That vendor has to be worth it.
Nokia is worth it (even without the DDP). Nokia actually shipped a great device previously (N900 running Maemo5) and is committed to shipping the N9. That is a lot more than other vendors can say. On top of that Nokia offers a great experience with the software and hardware. So as a user, Nokia is worth it. What about as a developer? Nokia has Qt Quick Components to provide reusable QML widgets. They also will support dependencies and have Python/PySide available in their store.
The Way Forward
For my initial ports I will continue to focus on what keeps this fun, writing apps for end-users despite any downsides of being vendor specific. After that I will bite the bullet of making my applications MeeGo compliant to make them available for a wider audience.
I have work to do on all of my applications but I will be focus on the ones that still use GTK first, especially since they are fairly simple. I am using QuickNote as my playground for learning QML. Expect further posts regarding progress updates and lessons learned regarding QML and MeeGo 1.2 Haramattan.
![]() |
The excitement increased as I saw the box personalized with my name regardless of that personalization just being a piece of tape |
My development plans have changed (and frustration increased) over time as the MeeGo development story has gotten refined. This is due to both changes in the UI toolkit availability and rules for distribution of applications.
Originally when things shifted to Qt I chose QWidget because it was ready now, includes widgets, and was recommended by Nokia (you know, the people who own Qt) for the types of apps I write (which recommendation was removed on June 22, 2011). At MeeGo Conference I found out that some (most? all?) MeeGo instances are not including QWdget themes (which without a theme its basically dead). QWidget in Qt5 will be optional and will probably not be included on those same MeeGo instances that currently don't ship a theme. So there goes that work.
I am still a bit frustrated with QML in that there is not even a subset of common QML Components between platforms though some disagree on it being a problem. I've just seen really terrible QML applications which mostly stem from everyone reinventing the same bugs when writing widgets from scratch. I also find it a wonderful experience having the widgets available for the desktop such that I can write apps in a desktop environment that are targeted for small screens without the need for any pesky IDEs and emulation. When it comes to testing, I am much more efficient testing and debugging natively on the desktop and just need to spot check how it translates to my devices.
I do not look forward to packaging for generic MeeGo. I found out at MeeGo conference that dependency management isn't guaranteed to be included and one has to include in their package any dependencies outside of core MeeGo. This is fine for compiled applications, you just pull in static libs. This is not as simple for Python developers where part of the beauty is your program isn't compiled and any compiled extensions are distributed and maintained separately. I have contemplated some tools to simplify this (based on cx-freeze and Hatchet) but that is a good amount of work and focused away from what makes all of this fun.
At MeeGo conference I raised my concern for packaging Python applications during the apps.meego.com lunch time BoF. The impression I got then (no clue if things have changed) is that they will focus on MeeGo compliant apps at first and might later support others. Even with that concession this is a stark contrast to the original vision that helped bring me on board from Maemo about (what was then called) Surrounds which would serve both as an app repo and as a place for third-party dependencies. If I chose to not bundle all of my libraries then I also am not available for any of the other distribution channels (and any contests they might be running). I don't program for the reward of the contests but it isn't fun feeling excluded.
The beauty of developing for Maemo was it had very low friction in the development story thanks to Python that I was able to spend more time focusing on what was fun: designing software for end-users to enjoy. I feel that this is being lost in MeeGo.
MeeGo and Nokia
The picture can be better when focusing on a individual vendors. Vendors may keep things a bit more lax with their distribution channels. The problem is then you are customizing per vendor which requires device availability for testing and a lot more work. That vendor has to be worth it.
Nokia is worth it (even without the DDP). Nokia actually shipped a great device previously (N900 running Maemo5) and is committed to shipping the N9. That is a lot more than other vendors can say. On top of that Nokia offers a great experience with the software and hardware. So as a user, Nokia is worth it. What about as a developer? Nokia has Qt Quick Components to provide reusable QML widgets. They also will support dependencies and have Python/PySide available in their store.
The Way Forward
For my initial ports I will continue to focus on what keeps this fun, writing apps for end-users despite any downsides of being vendor specific. After that I will bite the bullet of making my applications MeeGo compliant to make them available for a wider audience.
![]() |
The default date. It's good to see somebody at Nokia has a sense of humor |
I have work to do on all of my applications but I will be focus on the ones that still use GTK first, especially since they are fairly simple. I am using QuickNote as my playground for learning QML. Expect further posts regarding progress updates and lessons learned regarding QML and MeeGo 1.2 Haramattan.
Saturday, May 28, 2011
Meego Conference First Impressions
I am grateful for the opportunity to have gone to MeeGo Conference this week.
As has been discussed, the keynote was a let down. I very much appreciate Jaffa's analysis of the keynote. The only thing I have to add is that there were rumors that the keynote was reworked at the last minute for various reasons and if this is the case we should be somewhat understanding, it is difficult pulling off a change like that.
So what were my favorite things about MeeGo Conference?
First, spending time with various members of the Maemo and MeeGo communities. It was a lot of fun spending time together whether in sessions, meals, trips around town, or playing werewolf together. It was great to see GeneralAntilles, Jaffa, Qole, sjgadsby, fiferboy, texrat, timsamoff, wazd, and many more.
My second favorite aspect of the conference was the collaboration. I caught word of an apps.meego.com QA lunch BoF which was good as I don't remember if there were any other community app developers at the table to provide input from that perspective. It was great to see the collaboration between geekygirldawn, xfade, lbt, bergie, and others.
The main problems I see with where we are at with maemo.org extras is (1) the monotonically increasing version numbers between repos, causing pain if you have a "1.3" in extras-testing but need to push through a "1.2.1" due to say Google changing an API making the app in extras completely worthless, (2) the friction in the workflow for providing both testing and user feedback, (3) noise in extras-testing due to lack of cleanup, and (4) use of extras-devel rather than a PPA. (3) and (4) are resolved, so hopefully we also get (1) and (2).
Another aspect to the collaboration that I participated in was thp's Python presentation. During the Q&A we had a bit of a discussion on viability for Python for various instances of Meego. I'll cover this more in another post.
The final part to collaboration which I got to observe was thp et al taking the loaner Exo PC's and creating a multi-touch pong game.
The third and final of my favorite parts of the conference was the sessions.
Arjan Van De Ven's presentation was the best I saw due to his passion, his call to action, and audience (particularly Jaffa) raising issues with the internal communication within Intel being at friction with keeping MeeGo open. A couple months ago he announced replacing several pieces of key software with other variants. People took issue with no previous open discussion, few if any bug reports regarding Intel's complaints, possible NIH syndrome, and how much of this was spite towards Nokia for the switch. Some interesting tidbits from what little I caught of that flame war were that some of the software being reverted to (evolution) has the same problems as what it is replacing (tracker) and the suggestion that software shouldn't be accepted until it is ready being at odds with the call to use innovative technologies. Add on top of that last one the risk of "ready software" being more likely to be announced through unilateral big-reveals, shunning the in-the-open from-the-beginning software (here is looking at you MeeGo QML Components which I'll discuss more in a follow up post). The main disappointment were a couple of potshots made at Nokia during the discussion.
I do understand that there is a conflict between always-innovative/always-beta software and getting quality releases out now using tried and true software. I think the main thing people take issue with is how this was done and how much more this will be done with Nokia taking a backseat role.
The presentations on the future of Qt were also great even if they did raise my eyebrows on a few points (again, in a follow up post). I got to see the Qt5 plans, Lighthouse, and QtQuick 2 presentations. Unfortunately it wasn't until after-the-fact that I found out I should have also gone to MeeGo UX Components presentation (again, that promised follow up post).
As I've previously mentioned, thp's Python presentation was great. I also expect wazd's presentation to be good but forgot to go (and he even used some of my programs, shame on me). I also have to say I was impressed with hopbeat's use of Prezi and would wish to never see a traditional power point again.
Despite the let downs, MeeGo Conference was great. It was also good to get some time dedicated to programming and to see more of San Francisco compared to my last visit (I was a lot less lost thanks to a real GPS).
As has been discussed, the keynote was a let down. I very much appreciate Jaffa's analysis of the keynote. The only thing I have to add is that there were rumors that the keynote was reworked at the last minute for various reasons and if this is the case we should be somewhat understanding, it is difficult pulling off a change like that.
So what were my favorite things about MeeGo Conference?
First, spending time with various members of the Maemo and MeeGo communities. It was a lot of fun spending time together whether in sessions, meals, trips around town, or playing werewolf together. It was great to see GeneralAntilles, Jaffa, Qole, sjgadsby, fiferboy, texrat, timsamoff, wazd, and many more.
My second favorite aspect of the conference was the collaboration. I caught word of an apps.meego.com QA lunch BoF which was good as I don't remember if there were any other community app developers at the table to provide input from that perspective. It was great to see the collaboration between geekygirldawn, xfade, lbt, bergie, and others.
The main problems I see with where we are at with maemo.org extras is (1) the monotonically increasing version numbers between repos, causing pain if you have a "1.3" in extras-testing but need to push through a "1.2.1" due to say Google changing an API making the app in extras completely worthless, (2) the friction in the workflow for providing both testing and user feedback, (3) noise in extras-testing due to lack of cleanup, and (4) use of extras-devel rather than a PPA. (3) and (4) are resolved, so hopefully we also get (1) and (2).
Another aspect to the collaboration that I participated in was thp's Python presentation. During the Q&A we had a bit of a discussion on viability for Python for various instances of Meego. I'll cover this more in another post.
The final part to collaboration which I got to observe was thp et al taking the loaner Exo PC's and creating a multi-touch pong game.
The third and final of my favorite parts of the conference was the sessions.
Arjan Van De Ven's presentation was the best I saw due to his passion, his call to action, and audience (particularly Jaffa) raising issues with the internal communication within Intel being at friction with keeping MeeGo open. A couple months ago he announced replacing several pieces of key software with other variants. People took issue with no previous open discussion, few if any bug reports regarding Intel's complaints, possible NIH syndrome, and how much of this was spite towards Nokia for the switch. Some interesting tidbits from what little I caught of that flame war were that some of the software being reverted to (evolution) has the same problems as what it is replacing (tracker) and the suggestion that software shouldn't be accepted until it is ready being at odds with the call to use innovative technologies. Add on top of that last one the risk of "ready software" being more likely to be announced through unilateral big-reveals, shunning the in-the-open from-the-beginning software (here is looking at you MeeGo QML Components which I'll discuss more in a follow up post). The main disappointment were a couple of potshots made at Nokia during the discussion.
I do understand that there is a conflict between always-innovative/always-beta software and getting quality releases out now using tried and true software. I think the main thing people take issue with is how this was done and how much more this will be done with Nokia taking a backseat role.
The presentations on the future of Qt were also great even if they did raise my eyebrows on a few points (again, in a follow up post). I got to see the Qt5 plans, Lighthouse, and QtQuick 2 presentations. Unfortunately it wasn't until after-the-fact that I found out I should have also gone to MeeGo UX Components presentation (again, that promised follow up post).
As I've previously mentioned, thp's Python presentation was great. I also expect wazd's presentation to be good but forgot to go (and he even used some of my programs, shame on me). I also have to say I was impressed with hopbeat's use of Prezi and would wish to never see a traditional power point again.
Despite the let downs, MeeGo Conference was great. It was also good to get some time dedicated to programming and to see more of San Francisco compared to my last visit (I was a lot less lost thanks to a real GPS).
Friday, February 11, 2011
And what do I do now?
I felt a great disturbance in the Force, as if millions of voices suddenly cried out in terror and were suddenly silenced. I fear something terrible has happened.That quote was the first thing that went through my mind when I heard the announcement about the MS/Nokia partnership. As a community member I feel betrayed but I feel even worse for the employees. Nokia has been building up a strategy that attracts a very passionate group. The main supplier of your mobile platform switching strategies away from your needs is a drop in the bucket to your employer switching strategies to the exact opposite of where your passion lies. Whether you quit, get layed off, switch to developing a product you dislike, or even move to a project in the forgotten shed that gets no attention are all options that are not fun.
So drop in the bucket time.
I first switched to Nokia Linux-based products from Palm because I needed pocketable internet access. I was moving to a lot of internet services so my Palm wasn't doing a good job fulfilling my needs. I was annoyed with the Linux support which was purely a community effort. Momentum is important so that I will have the apps I need and the lifetime to make the switch worth while. I bought the 770 more as an experiment and found it fulfilled my needs.
I started developing applications for it to fit different needs and have come to appreciate it as a development platform. I can develop and use my applications on the desktop and have them also work on Maemo. That makes development easier and means I can make greater use of my work. I've also come to appreciate a flexible architecture with things like Telepathy allowing VOIP/IM service to be first-class, sharing system, libsocialweb (Meego), and hopefully in the future libfolks for contact aggregation.
Writing mobile applications has grown beyond fulfilling my needs as a user. I enjoy the opportunity to provide benefit for people as well as the chance to experiment and play with things that I don't get to in my job. Releasing my applications for free changes the feeling of user support, fixes, and new features from have-to to get-to and keeps things very liberating and fun.
Where do I go from here?
Meego: Intel is sticking with Meego. I am curious to see what all devices are announced next week. If they look like they have life to them then maybe I'll stick around. Nokia is still keeping Meego development around as a project and is releasing a device this year. I wonder if this is to fulfill some item in a contract with Intel. If so and when the contract ends, this is dead. If not, I wonder how long it will survive. I am sure the financial analysts will be telling them to kill it to shift even greater investment to money pot they will expect WP7 to be.
Android: Android would probably offer me the apps I need. Linux being under the hood is meaningless when you don't have the full Linux experience. I feel no motivation to develop for it, either in Java, C++ and the Python supported seems grotesque at first brush. I dislike the siloed approach that apps focus on. You don't get the power of frameworks like Telepathy, libsocialweb, or libfolks where the backend is decoupled from the front end, allowing for choosing the front-end and back-ends that best fulfill your needs rather than sacrificing one over the other. I dislike the idea of a manufacturer holding back updates and feel too lazy to always be following the latest custom ROMs (or some of the feats you need to go through to load one on some devices). I've played with friends Android devices and follow high level developments in the Android community but I could be mistaken on some things either good or bad.
webOS, something else? WP7 and iOS are out of the question. webOS seems a bit limited in both apps and development platform but does offer "synergy" which sounds like it provides a similar experience of a plugin architecture that Maemo has. I feel a bit concerned over the momentum of the platform especially in terms of how well it'll stick around. Being from a single vendor adds to that risk and limits device options (which was why I was glad Nokia partnered with Intel to create Meego). I only have cursory knowledge of webOS and have never played with a device whether this biases me in its favor or against it is to be seen.
I think I'll wait for next week to see what Meego devices come out of MWC before making my decision.
In the mean time I'd be curious if people have recommendations:
- Pocketable (3-5 inches) devices with modern specs (Required)
- Google Voice / VOIP Support regardless of network connection (Required)
- Good support for desktop Linux (Required)
- Support for Google services (GMail, Reader, Calendar, etc) (Option because I'm used to using the mobile website)
- Access to a Linux command line with VIM and SSH (Optional but encouraged)
- Support for Python or some other higher level language (maybe I'd even be willing to venture into Javascript which always felt dirty to me). (Optional but encouraged)
- Apps run on both desktop and handheld without modification or emulation (Optional but encouraged)
- Official way to distribute free apps without charge (Optional but encouraged)
I'd also be curious what users of my applications will do. There have been some stalwarts on Maemo 4.1 (n800, n810). Will you all be sticking around or giving up on it? Will there be a similar stalwart community on Maemo 5 (n900)? If I move from Maemo to Meego and everyone jumps ship from Maemo 4.1 and Maemo 5 then there is no reason for me to put in the extra effort to maintain compatibility with Maemo.
Friday, January 07, 2011
Porting Dialcentral: The Last "10%"
I had thought I was "almost done" with the rewrite of DialCentral from GTK to Qt. I've even started investigating my Meego ports and started helping package / develop a new project. Oh the joy of rewriting software and reimplementing old bugs and the creation of all new ones.
Maemo 4.1 was leading in being the least polished. Somehow I kept missing the setIconSize method on QTabWidget to override the default tab size on that platform which was incredibly small.
I also had to fix some modality issues. I had not set the parent window of my dialogs because rather than a new window appearing, the dialog's contents would be drawn on the parent window on top of everything else. It took me a bit to discover the variant of setParent that I also needed to set the Qt::WindowsFlags which you can conveniently do both with a setParent overload. The other side of this modality issue was with close buttons on Ubuntu on tap-out-of-window on Maemo 5, I forgot close buttons on some dialogs.
The last major user complaint is for those who haven't yet installed a Qt application on Maemo 4.1. The Application Manager reports the size of a package as its own size plus all of its dependencies. This makes sense for if a program has a separate data package or uses a rare but large library. The downside is if you see 5 packages listed at 24 MB "each" then that is a bit discouraging for installing any of them. Not much can be done about this one though.
I think most of the other Maemo 4.1 issues have been resolved or are "good enough".
Maemo 5 is now presenting the bigger challenge. Take a look at the following screenshot.
Problems:
I could just chalk this up as a Qt bug, file it, and move on. Even if I can find a simpler reproduction case what is the chance that Maemo 5 users will see the fix? Even if there is a chance that they do see it, how does that help them in the mean time? I've been experimenting with update, activate, enableUpdate with and without delays aimed to arrive after the slide-in animation and have not gotten any results. I might just have to queue up the UI changes for a timed delay after show to hope it is after the animation. This will take a bit of rewriting this dialog for a problem that is only on one platform. I'm not looking forward to this.
Maemo 4.1 was leading in being the least polished. Somehow I kept missing the setIconSize method on QTabWidget to override the default tab size on that platform which was incredibly small.
I also had to fix some modality issues. I had not set the parent window of my dialogs because rather than a new window appearing, the dialog's contents would be drawn on the parent window on top of everything else. It took me a bit to discover the variant of setParent that I also needed to set the Qt::WindowsFlags which you can conveniently do both with a setParent overload. The other side of this modality issue was with close buttons on Ubuntu on tap-out-of-window on Maemo 5, I forgot close buttons on some dialogs.
The last major user complaint is for those who haven't yet installed a Qt application on Maemo 4.1. The Application Manager reports the size of a package as its own size plus all of its dependencies. This makes sense for if a program has a separate data package or uses a rare but large library. The downside is if you see 5 packages listed at 24 MB "each" then that is a bit discouraging for installing any of them. Not much can be done about this one though.
I think most of the other Maemo 4.1 issues have been resolved or are "good enough".
Maemo 5 is now presenting the bigger challenge. Take a look at the following screenshot.
![]() |
If you can't tell "Tell Me" is my number of choice for testing DialCentral. |
- The box/line in the text box are an artifact of scrolling
- The "Cancel Call" button is marked as not-visible but the "Send SMS" and "Call" buttons are marked as visible. Sure doesn't look that way in the UI. Well, until you press on part of "Cancel Call", then the "Call" button is drawn on top.
I could just chalk this up as a Qt bug, file it, and move on. Even if I can find a simpler reproduction case what is the chance that Maemo 5 users will see the fix? Even if there is a chance that they do see it, how does that help them in the mean time? I've been experimenting with update, activate, enableUpdate with and without delays aimed to arrive after the slide-in animation and have not gotten any results. I might just have to queue up the UI changes for a timed delay after show to hope it is after the animation. This will take a bit of rewriting this dialog for a problem that is only on one platform. I'm not looking forward to this.
Saturday, December 11, 2010
Porting DialCentral: Meaningless Results
I'm not sure why I like to wrap up each port with these meaningless results but here we go.
Qt Version
I've still got some things to clean up, a couple bugs to fix, and improving some UI glitches on Maemo 4.1. After that my plan is to get all of my applications into the Community OBS so they are available for Meego.
Qt Version
- Near feature parity with GTK version (no custom callbacks, not re-implemented Alarm UI, no rotation for now)
- Access to call cancel
- Divider on various lists (for time ones, tried to balance heavy/light phone users)
- Condensed history
- Changed SMS Entry letter count style from GV to Nokia
- Tabs are cached between launches
- Display when history/messages tabs where last refreshed
- 4,866 LOCs
- From August 18th to December 2nd
- 7,207 LOCs
- For forever :)
I've still got some things to clean up, a couple bugs to fix, and improving some UI glitches on Maemo 4.1. After that my plan is to get all of my applications into the Community OBS so they are available for Meego.
Friday, December 10, 2010
Threading in Dialcentral
So this is a bit more boring of a post. Pretty much everything went as expected. The only annoyance as part of my zeal to avoid concrete inheritance was that Maemo 4.1 seems to have too old of a version of Qt to avoid inheriting from QThread. That page has some better practice listings for QThread.
DialCentral original used threading to be lazy about startup and to avoid the slowest of operations from blocking the UI thread. I've now pushed all blocking operations to a thread. The main question was what approach I would take to threading in PyQt. I was tempted to keep using Python threads using GObject callbacks but I keep to one toolkit for simplicity.
In The One Ring I had some code that used generators to trampoline code to the worker thread. I just had to refactor it so that there were toolkit specific and toolkit agnostic components.
I was glad that python logging works smoothly with QThreads. The messages get marked with some kind of distinguishing identifier.
See, nice and boring. No real issue which to me is a plus.
DialCentral original used threading to be lazy about startup and to avoid the slowest of operations from blocking the UI thread. I've now pushed all blocking operations to a thread. The main question was what approach I would take to threading in PyQt. I was tempted to keep using Python threads using GObject callbacks but I keep to one toolkit for simplicity.
In The One Ring I had some code that used generators to trampoline code to the worker thread. I just had to refactor it so that there were toolkit specific and toolkit agnostic components.
I was glad that python logging works smoothly with QThreads. The messages get marked with some kind of distinguishing identifier.
See, nice and boring. No real issue which to me is a plus.
Thursday, December 09, 2010
Latest Lessons on Widgets from Dialcentral
This is a bit of a catch-all post to go back over some previously covered items with some new things learned.
TreeViews
I previously couldn't find the way to do custom cell renderers. In part the oddity is that they provide default ways to render that are meaningless when you provide Delegates. I did some searching and found an example HTML Delegate. It had some issues with dark themes so I had to fix several bugs in it.
Next I wanted Dialcentral to have letter separators in the contact list. I never messed with hierarchical views in GTK before so I can't really compare. My first time branching out to applying this to Messages and History turned out bad because it seems to have the parent items' columns aligned with the child's columns. The original History view had 3 columns with the left two scrunched together. These didn't provide much space for the parent row's text. When I switched to a condensed history view this became moot.
When learning about hierarchical views I found out that the root expander decorator is given space by default even if there are no children and it isn't shown. This saved some space in the UI and will clean up the look of my other applications.
Layouts
The only real comment I have for layouts regards QGridLayout which I've not used before this. Early on I was looking at using my pie menus for the buttons which had some issues, so I setup the alignment. This didn't help but when I switched to buttons I eventually found out that it caused the buttons to fit to their minimum size rather than expand. I probably fixed the pie menu issue without realizing I had thwarted myself.
Combo Box
The only real trouble I ran into with QComboBox was when I wanted it editable. I was going to use this to allow choosing an existing GV configured callback or a custom one. On the desktop the only way I've gotten it to insert into the list the item I'm creating is when there is nothing else to capture the enter key press. Otherwise what gets activated is the "Apply" button (or worst "Clear Account" button when I remove the "Apply" button). I've not found my way around this issue yet.
TreeViews
![]() |
HTML Delegates |
![]() |
Simple heirarchy |
![]() |
Condensed history view, allowing the parent text to fit. |
Layouts
The only real comment I have for layouts regards QGridLayout which I've not used before this. Early on I was looking at using my pie menus for the buttons which had some issues, so I setup the alignment. This didn't help but when I switched to buttons I eventually found out that it caused the buttons to fit to their minimum size rather than expand. I probably fixed the pie menu issue without realizing I had thwarted myself.
Combo Box
The only real trouble I ran into with QComboBox was when I wanted it editable. I was going to use this to allow choosing an existing GV configured callback or a custom one. On the desktop the only way I've gotten it to insert into the list the item I'm creating is when there is nothing else to capture the enter key press. Otherwise what gets activated is the "Apply" button (or worst "Clear Account" button when I remove the "Apply" button). I've not found my way around this issue yet.
![]() |
Callback Combobox caused problems on desktop Qt |
Wednesday, December 08, 2010
Porting Dialcentral to Qt
![]() |
GrandCentral Dialer |
![]() |
DialCentral 1.0.7 |
![]() |
DialCentral 1.2 Beta |
For me the interesting part is this was my first Qt app that involves threading. Related to this is that I want to maintain as much common code with The One Ring as possible. Also with more UI work comes more insight into the better ways of doing things. I try to keep all of my UIs fairly simple but this is one of the more involved ones.
Sunday, November 21, 2010
Dublin and Meego Conference
Thanks to the generosity of the Linux Foundation I flew out and attended Meego Conference this year as well as payed for a couple extra days to see Dublin. This is my first time out of the United States (I did pop across the border into western Canada for 10 minutes but I don't think that counts). I've been a Maemo user since the 770 but I never before attended any of the Maemo Summits. This was a great experience.
On Day 1 I attended Rob Bradford's location/social aware presentation. I had not known about libsocialweb before which I thought was interesting. More interesting is I got some ideas of things to do with geoclue. I also went to a presentation about the Touch Input Method Framework. The thing that saddens me about these frameworks being created is that they are in-process and limit you to developing in C++ unless someone goes through the effort for each one to write a proxy plugin. I try to exclusively deal with Python because it let's me focus more of my time on developing rather than worrying about cross-compiling, debugging, etc. I can understand that there can be performance issues for some frameworks passing a lot of data over dbus, that was the reason I was given for libfolk. At the evenings receptions, timeless and I had some interesting conversation. I just hope I remember all the UI feedback he gave.
On Day 2 I attended the two telepathy related sessions (the project, not the super power though that would have made an interesting and confusing unconference day session). I am looking forward to telepathy 1.0 despite the extra work it is going to put on me for updating The One Ring (and any future CMs I write). It will be disappointing if I end up having to drop a couple of platforms (currently I support Maemo 4.1, Maemo 5, and desktop Ubuntu with work being done to prep for Meego).
Some worries of fcrochik and I were eased a bit when Mikhail brought up the QtContact plugin backed by libfolk (a bit before a blogpost hit the various planets). It still seems odd that QtContacts is built with support for plugins yet you would either need some kind of layer on top to aggregate the plugins (needing retrofits for apps using the old API) or else only have one plugin and it aggregates things that plugin to it (plugin for plugins, joy).
The third day was unconference which is more of a good-chaotic everyone-present-what-they-want kind of day. Sadly due to a conflict I missed Christian's Wayland presentation.
During one of the slots is when I picked up my Lenovo Ideapad that they were giving away. A bit weird that it comes with a UK power chord and keyboard (I suspect I'm not the only American that set it to US layout to our confusion). Sadly there is still some work that needs being done which is not giving people the best perception of the netbook/tablet and of Meego. Hopefully it'll all be worked out in the thread on f.m.c.
I ended the unconference with leading a session about experiences porting from Maemo to Meego but due to conflicts and being the end of the last day I only had fcrochik in attendance. We still had the good time.
Being from the US and having seen pretty much no professional football games, I enjoyed the Ireland v Norway game. I heard from others later that both teams weren't playing all that well. We had free tickets, the pre-game premium lounge with drinks and pretty good snacks. We had a good number of rounds of the food because they came in such small bowls. Thankfully texrat was nearby to take all of our dishes. I didn't take part much in the half-time or pot-game festivities since the lines were long.
I have to say the venue was great, having the hacker's lounge was great, and it all felt top notch. Amy, Dawn, Quim, and everyone else involved did a great job and there are many thanks to go around. One lesson learned I think was to have more power (and maybe even power converters at the "Power Plant" for people like me who forgot one). If my laptop I brought actually had power, it would have been fun to have a day like what I heard happened in Barcelona.
I enjoyed being out in Dublin on the other days. I managed to do everything by foot besides going from and to the airport (so 10+ km of walking on non-conference days).
On Saturday I spent my time at the city centre. I saw Marions Square, St Stephen's Green, Dublin Castle, Christ Church Cathedral, and walked Grafton Street. I enjoyed exploring the city but none of those sites really moved me. I had some good lamb stew at some place near Dublin Castle. Coming from Austin and freezing out in Dublin, that stew hit the spot. That evening I also went and saw Tosca performed at the Gaiety Theatre. That was a very powerful production.
On the Thursday after the Conference I ended up going to some of the further out spots. I saw the National Gallery and then walked out to O'Connell Street to see the Spire, the James Joye Statue, the Remembrance Garden (very beautiful both in design and in idea), and the James Joyce Centre. I then went out west and saw the Leprechaun Museum on my way. Eventually I was at Kilmainham Gaol and the Ireland Museum of Modern Art. I then had to rush back real quick to get on the internet to see when I flew back.
Those last three sites ended up being my favorite. It was fascinating to learn the political history of Ireland from the perspective of a jail, including the execution of the leaders of the Easter Rising. The Garden of Remembrance was a great place to contemplate. The Leprechaun Museum was fascinating for learning Irish Mythology.
The things I sadly didn't get to see are the Ireland Botanical Gardens, Mallahide Castle, Knowth/Newgrange, the War Memorial Gardens, and a Famine Memorial I saw on my tour map (if it exists, couldn't find it). I wasn't even sure how I would get to Knowth/Newgrange but at the Leprechaun Museum they aid that the Dublin Tourism place on O'Connell does a tour up in those parts, you jut need to arrange it a head of time it sounded like. Something to know for the next trip out to Dublin.
I find it interesting that a friend had recommended I see a lot of the Churches but instead I saw or at least tried to see war memorials, jails, execution sites, ancient burial tombs, etc. A bit different of priorities I guess.
See Pictures
I arrived on Saturday the 13th and left on Friday the 19th. I ended up site seeing rather than participating in any of the early bird events.
On Day 1 I attended Rob Bradford's location/social aware presentation. I had not known about libsocialweb before which I thought was interesting. More interesting is I got some ideas of things to do with geoclue. I also went to a presentation about the Touch Input Method Framework. The thing that saddens me about these frameworks being created is that they are in-process and limit you to developing in C++ unless someone goes through the effort for each one to write a proxy plugin. I try to exclusively deal with Python because it let's me focus more of my time on developing rather than worrying about cross-compiling, debugging, etc. I can understand that there can be performance issues for some frameworks passing a lot of data over dbus, that was the reason I was given for libfolk. At the evenings receptions, timeless and I had some interesting conversation. I just hope I remember all the UI feedback he gave.
On Day 2 I attended the two telepathy related sessions (the project, not the super power though that would have made an interesting and confusing unconference day session). I am looking forward to telepathy 1.0 despite the extra work it is going to put on me for updating The One Ring (and any future CMs I write). It will be disappointing if I end up having to drop a couple of platforms (currently I support Maemo 4.1, Maemo 5, and desktop Ubuntu with work being done to prep for Meego).
Some worries of fcrochik and I were eased a bit when Mikhail brought up the QtContact plugin backed by libfolk (a bit before a blogpost hit the various planets). It still seems odd that QtContacts is built with support for plugins yet you would either need some kind of layer on top to aggregate the plugins (needing retrofits for apps using the old API) or else only have one plugin and it aggregates things that plugin to it (plugin for plugins, joy).
The third day was unconference which is more of a good-chaotic everyone-present-what-they-want kind of day. Sadly due to a conflict I missed Christian's Wayland presentation.
During one of the slots is when I picked up my Lenovo Ideapad that they were giving away. A bit weird that it comes with a UK power chord and keyboard (I suspect I'm not the only American that set it to US layout to our confusion). Sadly there is still some work that needs being done which is not giving people the best perception of the netbook/tablet and of Meego. Hopefully it'll all be worked out in the thread on f.m.c.
I ended the unconference with leading a session about experiences porting from Maemo to Meego but due to conflicts and being the end of the last day I only had fcrochik in attendance. We still had the good time.
Being from the US and having seen pretty much no professional football games, I enjoyed the Ireland v Norway game. I heard from others later that both teams weren't playing all that well. We had free tickets, the pre-game premium lounge with drinks and pretty good snacks. We had a good number of rounds of the food because they came in such small bowls. Thankfully texrat was nearby to take all of our dishes. I didn't take part much in the half-time or pot-game festivities since the lines were long.
I have to say the venue was great, having the hacker's lounge was great, and it all felt top notch. Amy, Dawn, Quim, and everyone else involved did a great job and there are many thanks to go around. One lesson learned I think was to have more power (and maybe even power converters at the "Power Plant" for people like me who forgot one). If my laptop I brought actually had power, it would have been fun to have a day like what I heard happened in Barcelona.
I enjoyed being out in Dublin on the other days. I managed to do everything by foot besides going from and to the airport (so 10+ km of walking on non-conference days).
On Saturday I spent my time at the city centre. I saw Marions Square, St Stephen's Green, Dublin Castle, Christ Church Cathedral, and walked Grafton Street. I enjoyed exploring the city but none of those sites really moved me. I had some good lamb stew at some place near Dublin Castle. Coming from Austin and freezing out in Dublin, that stew hit the spot. That evening I also went and saw Tosca performed at the Gaiety Theatre. That was a very powerful production.
On the Thursday after the Conference I ended up going to some of the further out spots. I saw the National Gallery and then walked out to O'Connell Street to see the Spire, the James Joye Statue, the Remembrance Garden (very beautiful both in design and in idea), and the James Joyce Centre. I then went out west and saw the Leprechaun Museum on my way. Eventually I was at Kilmainham Gaol and the Ireland Museum of Modern Art. I then had to rush back real quick to get on the internet to see when I flew back.
Those last three sites ended up being my favorite. It was fascinating to learn the political history of Ireland from the perspective of a jail, including the execution of the leaders of the Easter Rising. The Garden of Remembrance was a great place to contemplate. The Leprechaun Museum was fascinating for learning Irish Mythology.
The things I sadly didn't get to see are the Ireland Botanical Gardens, Mallahide Castle, Knowth/Newgrange, the War Memorial Gardens, and a Famine Memorial I saw on my tour map (if it exists, couldn't find it). I wasn't even sure how I would get to Knowth/Newgrange but at the Leprechaun Museum they aid that the Dublin Tourism place on O'Connell does a tour up in those parts, you jut need to arrange it a head of time it sounded like. Something to know for the next trip out to Dublin.
I find it interesting that a friend had recommended I see a lot of the Churches but instead I saw or at least tried to see war memorials, jails, execution sites, ancient burial tombs, etc. A bit different of priorities I guess.
See Pictures
Monday, August 16, 2010
Porting ejpi: Meaningless Results
So let's see how the two versions of ejpi compare:
Qt Version
The LOC counts look nice, more drastic than my Gonvert port for what it is worth. I had a lot more distractions going on during my port of ejpi so a comparison of implementation times between Gtk/Qt or Gonvert/ejpi is as meaningless as the title of this post suggests.
My main Qt disappointment in this port is what seems like high coupling both in Qt itself and in code written by others (the example pie menus).
Now for some of the upsides. Though I implemented them differently, the similarity in the drawing code between Qt and Cairo helped. Also finding out about QStandardItemModel made my life easier.
I'm split on which direction I want to go in my learning of Qt. Originally I had planned on porting Dialcentral for gaining experience with threading. I have too many ideas though and not enough time. Possible directions include: porting other apps of mine, a new app using QGraphicsView, or some QtMobility contacts (and eventually calendar) plugins (though then I'd also have to learn the quirks of Qt in C++ and deploying compiled apps to Maemo).
Qt Version
- Feature parity with the GTK version
- Improved pie menu performance but two pie menu display bugs
- Simplified layout with the potential for rotation
- 3382 LOCs
- From June 8th to July 16th
- 4473 LOCs
- From January 30th to February 25th
The LOC counts look nice, more drastic than my Gonvert port for what it is worth. I had a lot more distractions going on during my port of ejpi so a comparison of implementation times between Gtk/Qt or Gonvert/ejpi is as meaningless as the title of this post suggests.
My main Qt disappointment in this port is what seems like high coupling both in Qt itself and in code written by others (the example pie menus).
Now for some of the upsides. Though I implemented them differently, the similarity in the drawing code between Qt and Cairo helped. Also finding out about QStandardItemModel made my life easier.
I'm split on which direction I want to go in my learning of Qt. Originally I had planned on porting Dialcentral for gaining experience with threading. I have too many ideas though and not enough time. Possible directions include: porting other apps of mine, a new app using QGraphicsView, or some QtMobility contacts (and eventually calendar) plugins (though then I'd also have to learn the quirks of Qt in C++ and deploying compiled apps to Maemo).
Thursday, July 29, 2010
Porting ejpi: Tree Models in Qt
For me, one thing that differentiates the quality of a calculator on a mouse or touch driven device is having a calculation history and having it be interactive. In Gtk I implemented this with a TreeView and a ListStore. I had never implemented a custom model in Gtk, the included models were simple enough and flexible enough that I didn't have the need.
When I ported Gonvert to Qt, all I could find information for was custom models with QAbstractItemModel and QTreeWidget/QTreeWidgetItem. QTreeWidgetItem's seem to only be good for Text. This worked for parts of Gonvert but not for others so I went through the complicated affair of implementing a QAbstractItemModel. I never felt like I fully understood the relationships of various items which usually worries me. I also found it slow due to all of the protection I "should" have in my code which causes a lot of calling back and forth between C++ and Python.
I'm unsure how I missed it but when I started on ejpi I came across QStandardItemModel/QStandardItem. This seems to at least allow a little more flexibility. I can put icons and checkboxes in as well as text into the model. I can also attach random python objects to each item. The downside to a stock model is that you have to externally maintain any invariants but I was already managing those with my Gtk version.
I would be curious about the various performance merits to each approach but I'll have to pass on that for now. The two main things I found frustrating was finding out about these and how each API was helpful in its own way, sometimes better, sometimes worse than its fellow approaches.
Most of my API issues were due to being unfamiliar with them and having to fight with the different approaches each model system took to presenting itself to the developer. I still have one unfortunate piece of code in ejpi's calucation history which implements peek through a pop and a push.
I've not messed with setting the font on any of these (I should have for Gonvert and also for ejpi) so I'm unsure how the font experience is in Qt. Mainly I'm interested in what formatting Pango html-esque tags allowed, changing relative size and basic formatting while keeping everything else to theme defaults. A quick look through the API seems to suggest this is simple but that is yet to be determined.
I still miss GTK's generic way of handling rendering with its use of CellRenderers. You don't have to be stuck with the built-in choices for representing items in your model. It also keeps presentation information away from the model.
I did enjoy how easy QStandardItem's made user edits (sadly only seems to work on the desktop) and activating of individual items. I didn't get around to exploring drag and drop for history re-arrangement (helpful for RPN calculators) but I don't think I implemented that for the GTK version either.
My GTK version and my Qt version of my ejpi history are available for comparison.
I guess the summary would be that overall I'm a fan of QStandardItemModel and wish I had found it sooner.
When I ported Gonvert to Qt, all I could find information for was custom models with QAbstractItemModel and QTreeWidget/QTreeWidgetItem. QTreeWidgetItem's seem to only be good for Text. This worked for parts of Gonvert but not for others so I went through the complicated affair of implementing a QAbstractItemModel. I never felt like I fully understood the relationships of various items which usually worries me. I also found it slow due to all of the protection I "should" have in my code which causes a lot of calling back and forth between C++ and Python.
I'm unsure how I missed it but when I started on ejpi I came across QStandardItemModel/QStandardItem. This seems to at least allow a little more flexibility. I can put icons and checkboxes in as well as text into the model. I can also attach random python objects to each item. The downside to a stock model is that you have to externally maintain any invariants but I was already managing those with my Gtk version.
I would be curious about the various performance merits to each approach but I'll have to pass on that for now. The two main things I found frustrating was finding out about these and how each API was helpful in its own way, sometimes better, sometimes worse than its fellow approaches.
Most of my API issues were due to being unfamiliar with them and having to fight with the different approaches each model system took to presenting itself to the developer. I still have one unfortunate piece of code in ejpi's calucation history which implements peek through a pop and a push.
I've not messed with setting the font on any of these (I should have for Gonvert and also for ejpi) so I'm unsure how the font experience is in Qt. Mainly I'm interested in what formatting Pango html-esque tags allowed, changing relative size and basic formatting while keeping everything else to theme defaults. A quick look through the API seems to suggest this is simple but that is yet to be determined.
I still miss GTK's generic way of handling rendering with its use of CellRenderers. You don't have to be stuck with the built-in choices for representing items in your model. It also keeps presentation information away from the model.
I did enjoy how easy QStandardItem's made user edits (sadly only seems to work on the desktop) and activating of individual items. I didn't get around to exploring drag and drop for history re-arrangement (helpful for RPN calculators) but I don't think I implemented that for the GTK version either.
My GTK version and my Qt version of my ejpi history are available for comparison.
I guess the summary would be that overall I'm a fan of QStandardItemModel and wish I had found it sooner.
Porting ejpi: Icon Theming
ejpi includes some custom icons as well as using theme icons.
The custom icons are used for a couple of buttons. I know the path to the custom icons and I load them up and everything works as expected.
The stock icons are used for deleting items in the calculator's history as well as a severity indicator and close button for the error info box.
I ported the GTK code from using theme icon names to QIcon.fromTheme. It was fun for me to find out that fromTheme was added in Qt 4.6 (I still support Maemo 4.1 with my applications and it only has Qt 4.2 I think). How did applications do this before 4.6? I don't think I've used KDE since v2 but I think its generally supported icon theming along with everything else which precludes the application building all icons in as resources.
I also had fun with fd.o icon names not being available on all platforms so I had to find alternatives for each and put in fallback code.
I guess chock this up as another thing to abstract away when running on various platforms.
I'm not saying this was hard or anything but just another lessons learned in porting to Qt.
The custom icons are used for a couple of buttons. I know the path to the custom icons and I load them up and everything works as expected.
The stock icons are used for deleting items in the calculator's history as well as a severity indicator and close button for the error info box.
I ported the GTK code from using theme icon names to QIcon.fromTheme. It was fun for me to find out that fromTheme was added in Qt 4.6 (I still support Maemo 4.1 with my applications and it only has Qt 4.2 I think). How did applications do this before 4.6? I don't think I've used KDE since v2 but I think its generally supported icon theming along with everything else which precludes the application building all icons in as resources.
I also had fun with fd.o icon names not being available on all platforms so I had to find alternatives for each and put in fallback code.
I guess chock this up as another thing to abstract away when running on various platforms.
I'm not saying this was hard or anything but just another lessons learned in porting to Qt.
Saturday, July 17, 2010
"Antenna-gate", Fanboys, and Trolls
I just saw a good post on this from the Google Testing Bog.
Despite my dislike of Apple and their treatment of developers (and my disgust that developers are flocking to receive their flogging) I think we should be careful how we react to this. Problems happen. There is both good and bad to how Apple handled this. If we feel a bit annoyed in how they handled this we should have that same annoyance no matter the source rather than giving in to fanboy-ism
Do we look at this and think how much better our platform is but get annoyed when others point out the problems with our platform of choice?
This is a general problem whether it be long standing technology flamewars (VI is better btw), to religion, or to politics. Sometimes the very nature of having two groups forms an automatic antagonism (see also George Washington's Farewell address).
I find Krister Stendahl's rules for religious understanding to be very applicable to all of these areas of our life:
Despite my dislike of Apple and their treatment of developers (and my disgust that developers are flocking to receive their flogging) I think we should be careful how we react to this. Problems happen. There is both good and bad to how Apple handled this. If we feel a bit annoyed in how they handled this we should have that same annoyance no matter the source rather than giving in to fanboy-ism
Do we look at this and think how much better our platform is but get annoyed when others point out the problems with our platform of choice?
This is a general problem whether it be long standing technology flamewars (VI is better btw), to religion, or to politics. Sometimes the very nature of having two groups forms an automatic antagonism (see also George Washington's Farewell address).
I find Krister Stendahl's rules for religious understanding to be very applicable to all of these areas of our life:
- When you are trying to understand another religion, you should ask the adherents of that religion and not its enemies.
- Don't compare your best to their worst.
- Leave room for "holy envy."
Concurrently Beating a Dead Horse
Sometimes it seems like the horse is already dead but today I came across a fun article describing Erlang's concurrency model. This is no surprise because Actors (along with STM) seems to be a favorite on the various programming sites.
So why continue the beatings? I thought I'd solidify my thoughts by writing them out. I also find exploring these principles beneficial for my projects to improve design even if the high level implementations wouldn't fit within the architecture for some of them.
In any concurrency discussion I think its good to remind ourselves of the pillars of concurrency to help avoid silver-bullet-syndrome. I highly recommend Herb Sutter's Effective Concurrency series, especially the introductory article where he talks about Callahan's Pillars.
(sorry for the text image, that is how the content was stored in the original article)
STM
First two items of background. The first is that I'm one of those odd people who enjoys the area where hardware and software meet. The second is that my current job is in software for test and automation systems. Having to warn customers about the possibility of Death and Dismemberment is not pleasant.
With those in mind I tend to worry more about how a system, pattern, architecture, whatever talks to the real world than how parallel it can get. This leads me to dislike STM as my default model of concurrency to use because the world is not transactional. If you have a program controlling a swinging arm of death, what does a revert mean? One of Microsoft's researchers on STM.NET admits to this problem and many others (but again it does have its uses).
Dataflow
I enjoyed my experiences with digital design in college but at the moment it is not the field for me. Also VHDL and Verilog leave much to be desired.
I find the idea of applying the natural concurrency of digital hardware to software fascinating.
In the industry I work in there is a popular data flow programming language. For high level integration or large datasets it seems great to have the implicit parallelism. I wonder how much parallelism it really gets with small data sets in simple applications. The language has a couple draw backs to me for the projects I work on outside of work:
Now on to actors. I've always been a fan of the simple concept of handling concurrency through shared-nothing message-passing like Erlang. I can easily conceptualize how this would work with hardware. I enjoy the fact that each process is normally short-lived enough that the GC doesn't even need to be called. These combine to give it soft real-time which is a field I've always found fascinating.
How does it handle various models of concurrency? I like the idea of having access to an Actor's low level primitives in Erlang to more naturally implement something else like a finite-state-machine. The low-level message passing is events so you get that. I don't think I'm doing enough embarrassing parallel tasks to miss having dataflow. I bet you could model a form of STM for groups of actors with the error system.
Sadly I've never really done anything in Erlang because:
Events
Events aren't as high-level of a concurrency model as STM and Actors but they can sometimes have a lower barrier to entry, especially when integrating with certain frameworks. Previously all of my open source applications have been glib based and most were GTK based. I found glib events a pleasure to work with.
Simple UI callbacks work well. I could simplify more complex sequences of callbacks like with DBus by abstracting them away but I've tended to abstract the concept so I can do things like map/reduce DBus calls.
Registering idle and timeout events seem to be atomic which makes life great certain applications of parallelism. What I've tended to do is keep a thread for a group of data (like my connection state for Google Voice). I then push to a queue a task I want the thread to apply to the data (make a GV call, get contacts, etc) and I include a callback that is registered as an idle callback for when the data is ready. I have no management of locks to worry about.
I've even been able to abstract away the callbacks to keep good spatial locality on the logic in the code.
An example from The One Ring, my Telepathy Connection Manager for Google Voice:
@misc_utils.log_exception(_moduleLogger)
def RequestStreams(self, contactId, streamTypes):
"""
For org.freedesktop.Telepathy.Channel.Type.StreamedMedia
@returns [(Stream ID, contact, stream type, stream state, stream direction, pending send flags)]
"""
contact = self._conn.get_handle_by_id(telepathy.constants.HANDLE_TYPE_CONTACT, contactId)
assert self.__contactHandle == contact, "%r != %r" % (self.__contactHandle, contact)
le = gobject_utils.AsyncLinearExecution(self._conn.session.pool, self._call)
le.start(contact)
streamId = 0
streamState = telepathy.constants.MEDIA_STREAM_STATE_CONNECTED
streamDirection = telepathy.constants.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL
pendingSendFlags = telepathy.constants.MEDIA_STREAM_PENDING_REMOTE_SEND
return [(streamId, contact, streamTypes[0], streamState, streamDirection, pendingSendFlags)]
@misc_utils.log_exception(_moduleLogger)
def _call(self, contact):
contactNumber = contact.phoneNumber
self.__calledNumber = contactNumber
self.CallStateChanged(self.__contactHandle, telepathy.constants.CHANNEL_CALL_STATE_RINGING)
try:
result = yield (
self._conn.session.backend.call,
(contactNumber, ),
{},
)
except Exception:
_moduleLogger.exception("While placing call to %s" % (self.__calledNumber, ))
return
self._delayedClose.start(seconds=0)
self.CallStateChanged(self.__contactHandle, telepathy.constants.CHANNEL_CALL_STATE_FORWARDED)
In the DBus callback (as in, happens in the main-loop) RequestStreams I create an AsyncLinearExecution object (eh, couldn't think of something better for a name) that runs the generator _call as an idle callback (again, executes in the main-loop). The yield passes a function, args, and kwds to the thread passed into AsyncLinearExecution's __init__ for it to run without blocking the main-loop. When it finishes it takes the results and passes that out of the yield. I can even move the exception from the thread to be thrown at the yield.
I maintain AsyncLinearExecution as an object so I can "cancel" it at anytime. My style of cancellation is for the thread's results to be ignored. In the try/except this gets represented as a StopIteration exception. This style of cancellation greatly simplifies my shutdown logic for a Telepathy Connection.
I've not read too much about other event based systems for Python but if they don't have a main-loop I scratch my head as to how they integrate with threading and other features.
In conclusion:
So why continue the beatings? I thought I'd solidify my thoughts by writing them out. I also find exploring these principles beneficial for my projects to improve design even if the high level implementations wouldn't fit within the architecture for some of them.
In any concurrency discussion I think its good to remind ourselves of the pillars of concurrency to help avoid silver-bullet-syndrome. I highly recommend Herb Sutter's Effective Concurrency series, especially the introductory article where he talks about Callahan's Pillars.
(sorry for the text image, that is how the content was stored in the original article)
STM
First two items of background. The first is that I'm one of those odd people who enjoys the area where hardware and software meet. The second is that my current job is in software for test and automation systems. Having to warn customers about the possibility of Death and Dismemberment is not pleasant.
With those in mind I tend to worry more about how a system, pattern, architecture, whatever talks to the real world than how parallel it can get. This leads me to dislike STM as my default model of concurrency to use because the world is not transactional. If you have a program controlling a swinging arm of death, what does a revert mean? One of Microsoft's researchers on STM.NET admits to this problem and many others (but again it does have its uses).
Dataflow
I enjoyed my experiences with digital design in college but at the moment it is not the field for me. Also VHDL and Verilog leave much to be desired.
I find the idea of applying the natural concurrency of digital hardware to software fascinating.
In the industry I work in there is a popular data flow programming language. For high level integration or large datasets it seems great to have the implicit parallelism. I wonder how much parallelism it really gets with small data sets in simple applications. The language has a couple draw backs to me for the projects I work on outside of work:
- Single vendor (never a fan of lock-in)
- Except for the clunky implementation of Events, it does not handle other concurrency models which a task might be better for.
- Not designed well for general purpose programming situations
- Even though some applications seem fast to write the environment feels like it hinders me.
Now on to actors. I've always been a fan of the simple concept of handling concurrency through shared-nothing message-passing like Erlang. I can easily conceptualize how this would work with hardware. I enjoy the fact that each process is normally short-lived enough that the GC doesn't even need to be called. These combine to give it soft real-time which is a field I've always found fascinating.
How does it handle various models of concurrency? I like the idea of having access to an Actor's low level primitives in Erlang to more naturally implement something else like a finite-state-machine. The low-level message passing is events so you get that. I don't think I'm doing enough embarrassing parallel tasks to miss having dataflow. I bet you could model a form of STM for groups of actors with the error system.
Sadly I've never really done anything in Erlang because:
- At work when I do something in my language of choice I need to optimize for implementation time and not reduced run times which my investigation into Erlang seems to indicate it would be poor at.
- With my open source work it usually centers around another component for which I need to have a means of talking to. Python has a lot of bindings. I feel like with Erlang I'd have to roll my own.
Events
Events aren't as high-level of a concurrency model as STM and Actors but they can sometimes have a lower barrier to entry, especially when integrating with certain frameworks. Previously all of my open source applications have been glib based and most were GTK based. I found glib events a pleasure to work with.
Simple UI callbacks work well. I could simplify more complex sequences of callbacks like with DBus by abstracting them away but I've tended to abstract the concept so I can do things like map/reduce DBus calls.
Registering idle and timeout events seem to be atomic which makes life great certain applications of parallelism. What I've tended to do is keep a thread for a group of data (like my connection state for Google Voice). I then push to a queue a task I want the thread to apply to the data (make a GV call, get contacts, etc) and I include a callback that is registered as an idle callback for when the data is ready. I have no management of locks to worry about.
I've even been able to abstract away the callbacks to keep good spatial locality on the logic in the code.
An example from The One Ring, my Telepathy Connection Manager for Google Voice:
@misc_utils.log_exception(_moduleLogger)
def RequestStreams(self, contactId, streamTypes):
"""
For org.freedesktop.Telepathy.Channel.Type.StreamedMedia
@returns [(Stream ID, contact, stream type, stream state, stream direction, pending send flags)]
"""
contact = self._conn.get_handle_by_id(telepathy.constants.HANDLE_TYPE_CONTACT, contactId)
assert self.__contactHandle == contact, "%r != %r" % (self.__contactHandle, contact)
le = gobject_utils.AsyncLinearExecution(self._conn.session.pool, self._call)
le.start(contact)
streamId = 0
streamState = telepathy.constants.MEDIA_STREAM_STATE_CONNECTED
streamDirection = telepathy.constants.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL
pendingSendFlags = telepathy.constants.MEDIA_STREAM_PENDING_REMOTE_SEND
return [(streamId, contact, streamTypes[0], streamState, streamDirection, pendingSendFlags)]
@misc_utils.log_exception(_moduleLogger)
def _call(self, contact):
contactNumber = contact.phoneNumber
self.__calledNumber = contactNumber
self.CallStateChanged(self.__contactHandle, telepathy.constants.CHANNEL_CALL_STATE_RINGING)
try:
result = yield (
self._conn.session.backend.call,
(contactNumber, ),
{},
)
except Exception:
_moduleLogger.exception("While placing call to %s" % (self.__calledNumber, ))
return
self._delayedClose.start(seconds=0)
self.CallStateChanged(self.__contactHandle, telepathy.constants.CHANNEL_CALL_STATE_FORWARDED)
In the DBus callback (as in, happens in the main-loop) RequestStreams I create an AsyncLinearExecution object (eh, couldn't think of something better for a name) that runs the generator _call as an idle callback (again, executes in the main-loop). The yield passes a function, args, and kwds to the thread passed into AsyncLinearExecution's __init__ for it to run without blocking the main-loop. When it finishes it takes the results and passes that out of the yield. I can even move the exception from the thread to be thrown at the yield.
I maintain AsyncLinearExecution as an object so I can "cancel" it at anytime. My style of cancellation is for the thread's results to be ignored. In the try/except this gets represented as a StopIteration exception. This style of cancellation greatly simplifies my shutdown logic for a Telepathy Connection.
I've not read too much about other event based systems for Python but if they don't have a main-loop I scratch my head as to how they integrate with threading and other features.
In conclusion:
- STM: Doesn't seem appropriate for any of my applications
- Dataflow: Digital hardware is fun but not a fan of the main implementation in software
- Actors: Cool but lack of integration with libraries limits my use of languages designed around it
- Raw Events: a lot of fun for my open source applications.
Thursday, July 15, 2010
Porting ejpi: Custom Widgets in Qt
Because of my off-repeated distaste for making custom widgets unless needed, the pie menus for ejpi are my first real custom widget. Learning to write custom widgets is the reason I chose to port ejpi.
I started off with the C++ pie menu code from Nokia's add-ons. I ported it to Python (I doubt anyone would package the binary and making Python bindings available for Maemo any time soon). While at it I simplified the code and made it more re-usable.
Qt Pie Menu Design Evolution
QtPieItem is the client-provided object to store everything related to a slice. The two subclasses are QtPieAction and QtPieMenu.
QtPieMenu works well as a subclass of QtPieItem for when you want submenus but doesn't make sense with the parent QtPieMenu. What does the weight, icon, and text mean? Why not use composition instead? Besides I don't even need sub-menus in my application. So I dropped this (could easily be added later).
QtPieAction starts to look a lot like QAction. I figured I could just use QActions for all of my slices with a wrapper to track the weight (slice weight / all slices weight gives the percentage of a circle a slice takes up).
Moving on to QtPieMenu...
The center of the menu and outside the menu cause a cancellation. I like actions being connected to the center (one of these actions could be a cancel). I also like to throw the mouse so I wanted an option for infinite outer radius.
Overall I was seeing there were several pieces of code in here that could be reused and built up to make several variants of Pie Menus, depending on what was wanted. So I broke out pie locations (QPieFiling, basically the Model), rendering (QPieArtist, kind of like a View), and then the actual widget (QPieDisplay, QPieButton, QPieMenu).
This worked really well. I do not use QPieMenu so oh well. A QPieButton can have its representation set with a QActionPieItem.
When the QPieButton is clicked it shows a QPieDisplay that is filled up with delicious goodness.
Life Implementing a Custom Widget
This was actually a fairly pleasant experience.
With the original GTK version of ejpi I struggled to grab the current theme's colors but never was able to. I followed Qt Pie Menu's example in grabbing colors and it actually worked! There was one bug in the original C++ code, they used a "dark" for unselected text when "mid" is what is called for but luckily Qt actually had great documentation on when to use what color.
The other major challenge on the desktop I had was sizing. The QtPieMenu only used sizeHint which gives a suggested default size for widgets. For the buttons I wanted a minimum size of what is needed to draw the item with a suggested size of the client specified size. I wanted to then listen to resizes and scale my box accordingly so the user would have button separators.
For this I overrode minimumSizeHint and resizedEvent. I have the suspicion that widgets are supposed to implement sizeHint, minimumSizeHint, and maximumSizeHint rather than setting the sizes directly to leave it in the wisdom of the client to possibly bypass the hints by setting the actual minimum, actual, and maximum sizes.
def minimumSizeHint(self):
return self._buttonArtist.centerSize()
@misc_utils.log_exception(_moduleLogger)
def resizeEvent(self, resizeEvent):
self.setButtonRadius(min(resizeEvent.size().width(), resizeEvent.size().height()) / 2 - 1)
QtGui.QWidget.resizeEvent(self, resizeEvent)
When checking how things ran on Maemo I ran into a couple of strange issues. So they wouldn't become modal I registered the menus as SplashScreen. I think the hacking around modality of popups was what killed performance on my GTK version.
Unlike the GTK version I was getting the frost effect to the whole window minus the square around the pie. That provided quite an awkward look. I didn't find anything for this so I just drop the round pies.
The other problem which is yet to be resolved is that if an edge button is selected the pie appears off-center from the user's click. The odd part to me is even the middle buttons have the pie go partially off-screen so it must be some kind of how much is it off the screen.
You can find the source code for these pie menus in ejpi's git repo.
I started off with the C++ pie menu code from Nokia's add-ons. I ported it to Python (I doubt anyone would package the binary and making Python bindings available for Maemo any time soon). While at it I simplified the code and made it more re-usable.
Qt Pie Menu Design Evolution
QtPieItem is the client-provided object to store everything related to a slice. The two subclasses are QtPieAction and QtPieMenu.
QtPieMenu works well as a subclass of QtPieItem for when you want submenus but doesn't make sense with the parent QtPieMenu. What does the weight, icon, and text mean? Why not use composition instead? Besides I don't even need sub-menus in my application. So I dropped this (could easily be added later).
QtPieAction starts to look a lot like QAction. I figured I could just use QActions for all of my slices with a wrapper to track the weight (slice weight / all slices weight gives the percentage of a circle a slice takes up).
Moving on to QtPieMenu...
The center of the menu and outside the menu cause a cancellation. I like actions being connected to the center (one of these actions could be a cancel). I also like to throw the mouse so I wanted an option for infinite outer radius.
Overall I was seeing there were several pieces of code in here that could be reused and built up to make several variants of Pie Menus, depending on what was wanted. So I broke out pie locations (QPieFiling, basically the Model), rendering (QPieArtist, kind of like a View), and then the actual widget (QPieDisplay, QPieButton, QPieMenu).
This worked really well. I do not use QPieMenu so oh well. A QPieButton can have its representation set with a QActionPieItem.
When the QPieButton is clicked it shows a QPieDisplay that is filled up with delicious goodness.
Life Implementing a Custom Widget
This was actually a fairly pleasant experience.
With the original GTK version of ejpi I struggled to grab the current theme's colors but never was able to. I followed Qt Pie Menu's example in grabbing colors and it actually worked! There was one bug in the original C++ code, they used a "dark" for unselected text when "mid" is what is called for but luckily Qt actually had great documentation on when to use what color.
The other major challenge on the desktop I had was sizing. The QtPieMenu only used sizeHint which gives a suggested default size for widgets. For the buttons I wanted a minimum size of what is needed to draw the item with a suggested size of the client specified size. I wanted to then listen to resizes and scale my box accordingly so the user would have button separators.
For this I overrode minimumSizeHint and resizedEvent. I have the suspicion that widgets are supposed to implement sizeHint, minimumSizeHint, and maximumSizeHint rather than setting the sizes directly to leave it in the wisdom of the client to possibly bypass the hints by setting the actual minimum, actual, and maximum sizes.
def minimumSizeHint(self):
return self._buttonArtist.centerSize()
@misc_utils.log_exception(_moduleLogger)
def resizeEvent(self, resizeEvent):
self.setButtonRadius(min(resizeEvent.size().width(), resizeEvent.size().height()) / 2 - 1)
QtGui.QWidget.resizeEvent(self, resizeEvent)
When checking how things ran on Maemo I ran into a couple of strange issues. So they wouldn't become modal I registered the menus as SplashScreen. I think the hacking around modality of popups was what killed performance on my GTK version.
Unlike the GTK version I was getting the frost effect to the whole window minus the square around the pie. That provided quite an awkward look. I didn't find anything for this so I just drop the round pies.
The other problem which is yet to be resolved is that if an edge button is selected the pie appears off-center from the user's click. The odd part to me is even the middle buttons have the pie go partially off-screen so it must be some kind of how much is it off the screen.
You can find the source code for these pie menus in ejpi's git repo.
Monday, July 12, 2010
Porting ejpi: Dynamic Layouts with (Py)Qt
Previous and Future ejpi UI
The current ejpi layout does not seem very dynamic
In the history column an error info box may appear that you can click away (custom made, not the new one available in the latest versions of GTK). The "Trigonometry" button brings up a Touch Selector for choosing which secondary keyboard you want visible.
Some problems with this layout:
Showing only one keyboard at a time will allow the buttons to be a bit nicer and gives more space for the history. Switching the secondary keyboard was two clicks away. With only one keyboard switches might be more frequent so we'll switch to tabs with icon representations for single click access (despite what I remember of the Maemo guidelines discouraging tabs). Moving the Push key out makes it available when any keyboard is selected and reduces the need to specialize the keyboard layouts based on orientation.
We've not changed too much in terms of how dynamic the UI is. The only change is on rotation we will need to switch the keyboards from being to the right of the history to being below the history.
Comparing GTK and Qt Layout systems
GTK's layout system centers around container widgets that provide holes for their children to populate. Also the HBox are VBox are very static. Their base class is either abstract or an interface so you have to actually swap out the class in the widget hierarchy to change between a horizontal and vertical layout.
The impression I have of Qt so far is that it separates out the concept of layout from the widget container. A way to think of it is the widget is the canvas to draw on while the layout constrains the sizes and locations of the things to be drawn. I actually removed a layout at one point and the widgets stayed visible and in the same location as before. You can have layout hierarchies but the API support for layouts is drastically limited as compared to widgets. Also the separate widget and layout hierarchies can complicate things.
Implementing My Dynamic Layouts
Of course when I got started I didn't understand the difference between the way they did layouts. I tried to implement everything as if it was GTK. I had complex layout hierarchies that I was trying to swap out at run time. It seems simple to change up a flat layout hierarchy but when you are trying to work off of an abstraction the API disparity between widgets and layouts increases the complexity. I tried multiple approaches to get it all to work to no avail.
I might have come across one possible approach but I ran into the need to do some casting because the API returned QLayoutItem proxies rather than proxies for the derived class QHBoxLayout, etc. With PyGTK I never had to worry about casting so when hitting it with PyQt I gave up in disgust at how nasty that feels in Python. Please note that I am a C++ developer by trade and have no problems with it in C++.
It was when I finally connected how layouts work in Qt that I made some progress. I would create intermediary widgets that I'd assign my layouts to. I could then add/remove/show/hide them all I wanted without issue. I think this is the way it was meant to be done. I think one of the reasons I hit a wall with this as compared to others is my abhorrence for inheriting from QWidget. In Gonvert I even changed part of my user experience because I could not access certain event information unless I inherited from the Qt widget.
Its kind of hard to do code samples without gutting my entire application and plopping it in here so I'll just provide one class and how it sets up its widget/layout hierarchy:
class QErrorDisplay(object):
def __init__(self):
self._messages = []
icon = QtGui.QIcon.fromTheme("gtk-dialog-error")
self._severityIcon = icon.pixmap(32, 32)
self._severityLabel = QtGui.QLabel()
self._severityLabel.setPixmap(self._severityIcon)
self._message = QtGui.QLabel()
self._message.setText("Boo")
icon = QtGui.QIcon.fromTheme("gtk-close")
self._closeLabel = QtGui.QPushButton(icon, "")
self._closeLabel.clicked.connect(self._on_close)
self._controlLayout = QtGui.QHBoxLayout()
self._controlLayout.addWidget(self._severityLabel)
self._controlLayout.addWidget(self._message)
self._controlLayout.addWidget(self._closeLabel)
self._topLevelLayout = QtGui.QHBoxLayout()
self._topLevelLayout.addLayout(self._controlLayout)
self._widget = QtGui.QWidget()
self._widget.setLayout(self._topLevelLayout)
self._hide_message()
@property
def toplevel(self):
return self._widget
...
Note the toplevel property. Kind of nasty but its what I use to insert this into the rest of the class hierarchy
self._controlLayout = QtGui.QVBoxLayout()
self._controlLayout.addWidget(self._errorDisplay.toplevel)
self._controlLayout.addWidget(self._historyView.toplevel)
self._controlLayout.addLayout(self._userEntryLayout)
Something else neat that I noticed is that in Qt, QHBoxLayout and QVBoxLayout are just used for convenience when working with QBoxLayout which dynamically supports switching from horizontal and vertical layouts. This made me happy with Qt for once. I can't imagine there being significance enough of a difference that they should be separate in GTK or if there is that a dynamically switchable layout couldn't be supported. I can't speak too positively of Qt or people might worry. I think its a disservice that Qt has QHBoxLayout and QVBoxLayout and recommends their usage. Do you really need an extra class to not have to type in one extra parameter into the constructor? Really?
if maeqt.screen_orientation() == QtCore.Qt.Vertical:
defaultLayoutOrientation = QtGui.QBoxLayout.TopToBottom
else:
defaultLayoutOrientation = QtGui.QBoxLayout.LeftToRight
self._layout = QtGui.QBoxLayout(defaultLayoutOrientation)
# Actual handling of rotation with setDirection not implemented yet
Conclusion
QVBoxLayout and QHBoxLayout are your enemy, QBoxLayout is your friend.
Attach layouts to QWidget's to make layout work easy. It is sad that PyQt requires the use of casting.
I think there is suppose to be some new UI stuff to make dynamic layouts with fancy transitions easy. As I will talk about when I discuss QTreeView's it is a bit annoying trying to figure out what is the recommended approach for what situation so I just went with (1) what I found documentation for and (2) what seemed familiar.
The current ejpi layout does not seem very dynamic
In the history column an error info box may appear that you can click away (custom made, not the new one available in the latest versions of GTK). The "Trigonometry" button brings up a Touch Selector for choosing which secondary keyboard you want visible.
Some problems with this layout:
- Buttons are oddly shaped
- The keys would have to be customized for portrait and landscape
- Showing two keyboards would take up too much space in portrait mode
- Not enough space left to increase the button text size
Showing only one keyboard at a time will allow the buttons to be a bit nicer and gives more space for the history. Switching the secondary keyboard was two clicks away. With only one keyboard switches might be more frequent so we'll switch to tabs with icon representations for single click access (despite what I remember of the Maemo guidelines discouraging tabs). Moving the Push key out makes it available when any keyboard is selected and reduces the need to specialize the keyboard layouts based on orientation.
We've not changed too much in terms of how dynamic the UI is. The only change is on rotation we will need to switch the keyboards from being to the right of the history to being below the history.
Comparing GTK and Qt Layout systems
GTK's layout system centers around container widgets that provide holes for their children to populate. Also the HBox are VBox are very static. Their base class is either abstract or an interface so you have to actually swap out the class in the widget hierarchy to change between a horizontal and vertical layout.
The impression I have of Qt so far is that it separates out the concept of layout from the widget container. A way to think of it is the widget is the canvas to draw on while the layout constrains the sizes and locations of the things to be drawn. I actually removed a layout at one point and the widgets stayed visible and in the same location as before. You can have layout hierarchies but the API support for layouts is drastically limited as compared to widgets. Also the separate widget and layout hierarchies can complicate things.
Implementing My Dynamic Layouts
Of course when I got started I didn't understand the difference between the way they did layouts. I tried to implement everything as if it was GTK. I had complex layout hierarchies that I was trying to swap out at run time. It seems simple to change up a flat layout hierarchy but when you are trying to work off of an abstraction the API disparity between widgets and layouts increases the complexity. I tried multiple approaches to get it all to work to no avail.
I might have come across one possible approach but I ran into the need to do some casting because the API returned QLayoutItem proxies rather than proxies for the derived class QHBoxLayout, etc. With PyGTK I never had to worry about casting so when hitting it with PyQt I gave up in disgust at how nasty that feels in Python. Please note that I am a C++ developer by trade and have no problems with it in C++.
It was when I finally connected how layouts work in Qt that I made some progress. I would create intermediary widgets that I'd assign my layouts to. I could then add/remove/show/hide them all I wanted without issue. I think this is the way it was meant to be done. I think one of the reasons I hit a wall with this as compared to others is my abhorrence for inheriting from QWidget. In Gonvert I even changed part of my user experience because I could not access certain event information unless I inherited from the Qt widget.
Its kind of hard to do code samples without gutting my entire application and plopping it in here so I'll just provide one class and how it sets up its widget/layout hierarchy:
class QErrorDisplay(object):
def __init__(self):
self._messages = []
icon = QtGui.QIcon.fromTheme("gtk-dialog-error")
self._severityIcon = icon.pixmap(32, 32)
self._severityLabel = QtGui.QLabel()
self._severityLabel.setPixmap(self._severityIcon)
self._message = QtGui.QLabel()
self._message.setText("Boo")
icon = QtGui.QIcon.fromTheme("gtk-close")
self._closeLabel = QtGui.QPushButton(icon, "")
self._closeLabel.clicked.connect(self._on_close)
self._controlLayout = QtGui.QHBoxLayout()
self._controlLayout.addWidget(self._severityLabel)
self._controlLayout.addWidget(self._message)
self._controlLayout.addWidget(self._closeLabel)
self._topLevelLayout = QtGui.QHBoxLayout()
self._topLevelLayout.addLayout(self._controlLayout)
self._widget = QtGui.QWidget()
self._widget.setLayout(self._topLevelLayout)
self._hide_message()
@property
def toplevel(self):
return self._widget
...
Note the toplevel property. Kind of nasty but its what I use to insert this into the rest of the class hierarchy
self._controlLayout = QtGui.QVBoxLayout()
self._controlLayout.addWidget(self._errorDisplay.toplevel)
self._controlLayout.addWidget(self._historyView.toplevel)
self._controlLayout.addLayout(self._userEntryLayout)
Something else neat that I noticed is that in Qt, QHBoxLayout and QVBoxLayout are just used for convenience when working with QBoxLayout which dynamically supports switching from horizontal and vertical layouts. This made me happy with Qt for once. I can't imagine there being significance enough of a difference that they should be separate in GTK or if there is that a dynamically switchable layout couldn't be supported. I can't speak too positively of Qt or people might worry. I think its a disservice that Qt has QHBoxLayout and QVBoxLayout and recommends their usage. Do you really need an extra class to not have to type in one extra parameter into the constructor? Really?
if maeqt.screen_orientation() == QtCore.Qt.Vertical:
defaultLayoutOrientation = QtGui.QBoxLayout.TopToBottom
else:
defaultLayoutOrientation = QtGui.QBoxLayout.LeftToRight
self._layout = QtGui.QBoxLayout(defaultLayoutOrientation)
# Actual handling of rotation with setDirection not implemented yet
Conclusion
QVBoxLayout and QHBoxLayout are your enemy, QBoxLayout is your friend.
Attach layouts to QWidget's to make layout work easy. It is sad that PyQt requires the use of casting.
I think there is suppose to be some new UI stuff to make dynamic layouts with fancy transitions easy. As I will talk about when I discuss QTreeView's it is a bit annoying trying to figure out what is the recommended approach for what situation so I just went with (1) what I found documentation for and (2) what seemed familiar.
Porting ejpi from GTK t Qt
"e**(j * pi) + 1 = 0" is the more complete name of this project. Throwing the "j" in their might be confusing but blame that on my engineering background. Why yes, I love that fact that Python follows the EE notation over the Mathematical notation.
The idea for ejpi was born from reading a complaint on planet.gnome.org about how computer calculators overly mimic handheld calculators much to their detriment. On a computer there are a lot more ways to interact with a program than a device with a one-line display and a fixed set of hardware buttons.
So ejpi is designed as an RPN calculator that has an interactive history. You can delete item and duplicate items. With a one-line calculator it is easy to forget where you are in a multistep calculation, so ejpi shows you not just the result but the equation that got you there.
A 4" device (n8x0) is limited on what all you can put on the screen and shrinking it down to 3.5" (n900) makes it even harder. Making matters worse is that even if the device has a qwerty keyboard (n800 doesn't) its not well suited for math. So I chose to experiment with pie-menus as the keyboard. Typical pie-menus behave more like a context menu and have the middle of the pie be cancel. I changed this up a bit to mimic my favorite keyboard for the Palm, MessagEase. The pies are buttons that if you just click will perform the center's operation. If you click-drag the pie will popup and then release in the direction of another symbol for it to perform that operation.
There is a trade-off in how much of the pie's contents the button shows. If you show too much then it is cluttered. If you show too little then the user has to explore every button to know what operations are supported and where.
Sadly on Maemo devices, the performance of the pie menus is not what I would like which makes this more of a toy program. I actually use the HP42 clone more than my own calculator
Unlike when I blogged about Gonvert, I am still working on the ejpi port. I've got all the basic functionality working except switching keyboards. I also plan on adding rotation support, directly editing history, some UI tweaks, and maybe one or two other things that crop up. If the performance of the pies is better I might add an algebraic entry mode to make ejpi more widely appealing.
So far I plan to write about my experience in writing custom widgets for Qt, handling of dynamic layouts, and the various tree models. We'll see what is left by the time I am finished.
The idea for ejpi was born from reading a complaint on planet.gnome.org about how computer calculators overly mimic handheld calculators much to their detriment. On a computer there are a lot more ways to interact with a program than a device with a one-line display and a fixed set of hardware buttons.
So ejpi is designed as an RPN calculator that has an interactive history. You can delete item and duplicate items. With a one-line calculator it is easy to forget where you are in a multistep calculation, so ejpi shows you not just the result but the equation that got you there.
A 4" device (n8x0) is limited on what all you can put on the screen and shrinking it down to 3.5" (n900) makes it even harder. Making matters worse is that even if the device has a qwerty keyboard (n800 doesn't) its not well suited for math. So I chose to experiment with pie-menus as the keyboard. Typical pie-menus behave more like a context menu and have the middle of the pie be cancel. I changed this up a bit to mimic my favorite keyboard for the Palm, MessagEase. The pies are buttons that if you just click will perform the center's operation. If you click-drag the pie will popup and then release in the direction of another symbol for it to perform that operation.
There is a trade-off in how much of the pie's contents the button shows. If you show too much then it is cluttered. If you show too little then the user has to explore every button to know what operations are supported and where.
Sadly on Maemo devices, the performance of the pie menus is not what I would like which makes this more of a toy program. I actually use the HP42 clone more than my own calculator
Unlike when I blogged about Gonvert, I am still working on the ejpi port. I've got all the basic functionality working except switching keyboards. I also plan on adding rotation support, directly editing history, some UI tweaks, and maybe one or two other things that crop up. If the performance of the pies is better I might add an algebraic entry mode to make ejpi more widely appealing.
So far I plan to write about my experience in writing custom widgets for Qt, handling of dynamic layouts, and the various tree models. We'll see what is left by the time I am finished.
Subscribe to:
Posts (Atom)