Qt Stylesheets Tutorial

September 10th, 2009

Stylesheets add spicy flavor to your boring Qt GUIs.

For a long time, Qt has allowed you to decorate your GUIs with CSS’ish style sheets. Inspired by the web, stylesheets are a great way to stylize your Qt GUI, but it seems that few people use them. In this tutorial, we’ll create an example dialog in Qt using Designer and stylesheets. This tutorial assumes that you can get around in Qt Designer, and that you understand a little about Qt layouts.

Step 1 Create a new empty form, named MyLoginForm.

Step 2 Add a QFrame to your form, and apply a vertical layout to your form (any layout will work actually: vertical, horizontal or grid). Give your form about 30 pixels of layout margin around its perimeter. Name the QFrame “mainFrame”. When dealing with stylesheets, it’s convenient to name your widgets in Designer, even if you don’t plan to use them by name in your code (we won’t be writing any code besides CSS in this tutorial).

Step 3 In Designer’s object inspector, right-click on the top-level entry in the tree (called “MyLoginForm”), and select “Change styleSheet…” from the menu. You’ll get a little editor dialog like this:

This is where we specify the style sheet for our form. You can put a style sheet on any widget in your form, but I prefer to do all my stylizing at the parent widget (“MyLoginForm” in this case). I prefer to do it this way because you’ll never have to go hunting to find your style sheet — it’s all in one place in your form. Since stylesheets cascade down to the child widgets, you can stylize any widget in your form from this point.

Side note: “CSS” stands for “Cascading Style Sheets”

Let’s type in some CSS into the style sheet editor, like this:

#MyLoginForm {
background: gray;
}

#mainFrame {
border: 3px solid gray;
border-radius: 40px;
background: white;
}

After clicking OK on the editor dialog, you should see this:

If your’s doesn’t look like this, you may have forgotten to change the parent widget’s name to “MyLoginForm” or the QFrame’s name to “mainFrame” (yes, capitalization does matter — Qt stylesheets are case sensitive). Or you may have mistyped something into the CSS dialog.

One cool feature is that you get to preview the style changes right as you make them. You don’t have to compile, save, or run anything. Designer does a very good job of showing your stylesheet changes live (WYSIWYG for you old-timers).

Let me explain what we just did. In CSS, a pound sign, ‘#’, in front of a name is how we stylize an individual widget by that name. In our example, #MyLoginForm identifies the parent widget (i.e., the background area). All we did there is give it a gray background with background: gray;.

For #mainFrame, we gave it a thick gray border, a white background, and rounded corners.

Step 4 Let’s add some widgets to make this dialog actually do something. Drag and drop a pair of QLineEdits, QLabels, and a single QPushButton on the form inside “mainFrame” and arrange them roughly like this:

Step 5 Now apply a grid layout to “mainFrame”. Just select “mainFrame” by clicking on it (taking care not to accidentally select one of the QLineEdits or QLabels instead). Then click the grid layout button in Designer’s toolbar (optionally, you can go to the menu bar and click “Form” -> “Lay Out in a Grid”, or just press Ctrl+5 for you keyboard hackers).

Then give your layout some margin. I used 50 pixels of margin and 15 pixels for both vertical and horizontal spacing.

This is what you should have now:

Step 6 Let’s stylize those boring QPushButton and QLineEdits. Add this to the style sheet for MyLoginForm:

QLineEdit {
padding: 1px;
border-style: solid;
border: 2px solid gray;
border-radius: 8px;
}

QPushButton {
color: white;
background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #88d, stop: 0.1 #99e, stop: 0.49 #77c, stop: 0.5 #66b, stop: 1 #77c);
border-width: 1px;
border-color: #339;
border-style: solid;
border-radius: 7;
padding: 3px;
font-size: 10px;
padding-left: 5px;
padding-right: 5px;
min-width: 50px;
max-width: 50px;
min-height: 13px;
max-height: 13px;
}

Notice that we didn’t use the pound sign this time. When you omit the pound sign, you are specifying a “class” of widgets to stylize instead of a single widget by name. So in this case, we stylized all widgets of type “QLineEdit” and “QPushButton” (and any widget that may inherit from those widgets too).

That gives the QPushButton a cool gradient look and rounds the edges of the QLineEdits, like this:

Step 7 Now let’s make that boring white background a gradient instead. Replace the “background: white;” line in the “#mainFrame” section with this instead:

background: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #eef, stop: 1 #ccf);

Now you should see this:

Step 8 Since stylizing is all about detail and little tweaks, let’s mess a bit with the label font and the background color by changing the background for “#MyLoginForm” to “background: white”, and adding this:

QLabel {
font-weight: bold;
font-size: 15px;
}

Now we get our finished product:

Isn’t it great how much you can do in Designer with style sheets? There are even things you can do with style sheets that can’t be done without writing lots of yucky C++ code. For example, you can change the border style of just a single side of a QFrame.

Oddities

There is one gotcha to keep in mind when using style sheets: Once you use a style sheet, it will often override other properties, like the “font” property or the “frameStyle” property. Keep this in mind when Designer changes properties you didn’t expect.

The other oddity with style sheets is that there’s no way to “include” external style sheet files into your widget’s style sheet. This means that you can’t have a common style sheet that gets applied to your entire application like you can in the web. I have a Qt patch that allows you to do this, but so far the Trolls haven’t done anything with it (to my knowledge). This would be immensely useful.

Cross Platform Note
A style sheet tutorial wouldn’t be complete without a comment on cross-platform issues. The dialog we just designed will look exactly the same on Linux, Windows, and Mac OS X because we stylized it so aggressively. If you want your widgets to look native on each platform, you should generally use style sheets sparingly, but if your goal is to have a consistent look across all platforms, style sheets are your best friend.

Conclusion

Now that I have discovered the power of style sheets in Qt, I use them whenever possible. What cool things have you done with Qt style sheets?

Mozy Woes on Mac

September 4th, 2009

Mozy has been working fine on my Mac for about 6 months (aside from the massive memory usage it requires during backup), but starting about a week ago, I can’t make Mozy finish (or even start) a backup. I’ve tried uninstalling it, removing all configs, reinstalling it, using the shipped config defaults, and I still get this error in /Library/Logs/Mozy.log:

INF (backup) Starting backup
INF (backup) Using 108.7 MB of memory
INF (backup) User ID is [snipped for paranoia's sake]
INF (backup) Checking for update
INF (backup) Connecting to [snipped for paranoia's sake]
INF (backup) Version is up-to-date (1.4.3.3)
INF (backup) Adding new history entry
INF (backup) Cleaning up temporary files
INF (backup) Loading encryption key
INF (backup) Loading machine information ...
INF (backup) Connecting to [snipped for paranoia's sake]
INF (backup) Loading configuration from the server
INF (backup) Connecting to [snipped for paranoia's sake]
INF (backup) Fetching the manifest hash from the server
INF (backup) Connecting to [snipped for paranoia's sake]
INF (backup) Server version is 1.3.0.19
INF (backup) Building backup set
ERR (backup) Churn is not running, exiting backup
INF (backup) Cleaning up, finishing
INF (backup) Updating existing history entry
INF (backup) Finished backup (Client error)
INF (backup) Backing up history
INF (backup) Using 108.9 MB of memory
INF (backup) Ending backup

Sometimes I see this as well:

ERR (churn) Failed to scan files, stopping: database is locked

What is “Churn”? What database is locked? I am running Time Machine as well. Does that matter? Any ideas out there? I know some of you used to work at Mozy. Does any of this ring familiar?

On another note, does anyone else notice that Mozy Backups take a lot of memory on Mac OS X?


(click for bigger picture)

Mozy sits this way for hours on my Mac in the rare cases that it does actually get a backup started.

Apple Win, Dave Fail

August 15th, 2009

I’ve been enjoying looking down my nose at the world ever since getting my Apple iMac, but today my haughty soul was humbled by a personal fail.

I bought some more RAM to upgrade it. The RAM works fine. I tested it in my Dell laptop. The iMac, however, rejected it like an English queen turning her nose up at a Big Mac. It turns out that Apple has some pretty strong recommendations when it comes to RAM specifications. In fact, their requirements are so subtle that most RAM vendors don’t even publish the specs that Apple requires. Oh well, better luck next time. I’ve got some RAM to sell now.

And now for the Win. The iMac RAM is pretty easy to upgrade. The first thing you do is lay the computer down on its face. Well, when you do that, you are looking at the bottom of the pedestal which normally sits face-down on the desk. Guess what? The bottom of the pedestal actually has instructions printed on it for replacing the RAM. That was awesome.

Apple: 1
Dave: 0

Ditching BAStats

July 10th, 2009

For several years I used BAStats on my blog to get visitor statistics. It gave me nifty info like which Google searches lead people to my blog. After a a few years, my blog pages started loading very slowly, annoyingly slow, and I finally tracked it down to BAStats (using MySQL’s “show processlist” command). The BAStats tables were getting pretty big (hundreds of thousands of rows), and when I disabled BAStats, my blog sped up dramatically. So now it’s gone for good, and I’m using Google Analytics instead.

So long BAStats, and thanks for all the fish.

Example Qt Chat Program

July 9th, 2009

A few months ago I gave a presentation to the BYU Unix Users Group on Qt GUI development. During the meeting, we created a simple chat room program called chatterbox. At the time I promised to post my source code with comments, a promise on which I am now making good.

Here is the code. Instructions for building and running it are below:

The client source code (chatterbox.zip)
The server source code (chatterboxd.zip)

Build Instructions

You’ll need to install Qt for your operating system, at least version 4.3 or newer. Make sure “qmake” is in your path. On Windows, you’ll also want Visual Studio 2008 Express. Then, download and unzip the two zip files above. I’ll wait here while you do that.

Building the Server

  1. Open your favorite terminal (on Windows, use “Qt command prompt” in the start menu)
  2. cd into the chatterboxd directory
  3. Run qmake (you should see no output if it suceeds)
  4. Run make (or nmake on Windows — it will produce a lot of output and hopefully run error free)
  5. Run ./chatterboxd (or debug\chatterboxd.exe on Windows)
  6. It should print Ready when it’s ready to go.

Building the Client

Build the client the same way. Note, it’s the chatterbox (no “d” on the end) directory.

What it Looks Like

Here’s what the client looks like when you run it:


Getting Help

If you can’t get this to build or are running into some other problem, just leave a comment below, and I’ll get to it as soon as possible.

Happy Independence Day

July 4th, 2009

To celebrate Independence Day, I flew my T-28 with red, white, and blue streamers around the park.

How long does it take to convert disk space to RAM?

June 21st, 2009

About 12 years.

12 years ago I bought a new computer with 3GB of hard drive space (yes, just three). Today, the average new computer has about 3GB of RAM. It took 12 years to convert all that disk space to RAM.

If this pattern holds, then by the year 2021 we should have computers with about 500GB of RAM.

Awesome.

More User Interface Critiques

June 14th, 2009

Today in Windows XP, I double clicked on a scanner icon under “My Computer” after installing a new scanner. Here’s the dialog that popped up:

A few gripes worth considering:

  • The phrase “Microsoft Word” is mentioned 4 times.
  • 3 out of 4 of my options have repeated text (once in black, and again in gray, in case you prefer gray text over black text)
  • The entire serial number of the scanner is shown in the dialog. I’m sure this was done to guarantee uniqueness, but what a pain for the user, eh?
  • What does the phrase “initiate a new scan” mean? Sounds like programmer speak.
  • What is the difference between the “Microsoft Word” with a document icon and the “Microsoft Word” with a camera icon?

Linux Desktop: How many more years?

June 9th, 2009

How many more years am I going to have to wait before I get a Linux desktop that doesn’t look like it was cobbled together?

Before I rant about the Fedora 11 user interface, I have to say that I am a huge fan of Linux. I write code for Linux all day at work (both user interface and back-end code), and I love it. Linux is great. However, the Linux desktop has not kept up with the quality of its back-end counterpart, and I want to talk about it today. Thanks for reading.

I installed Fedora 11 today, the latest and greatest that the Linux community has to offer. I opted to use the KDE version just for fun. Here’s what I got:

Random Fonts

When I logged in for the first time, my fonts were too large, so I opened the “System Settings” (by the way, that name is way too scary for lay users) and navigated to the fonts section. This is what I saw:

Font Settings in Fedora 11

First of all, I have to change the font size 8 times. Why? Because I don’t know what each of those 8 settings means, and I want to make sure I get them all. Why do I have a “Menu”, a “Toolbar”, and a “Small” font? If you’re going to give this much fine grained control, you have to at least show screenshots of where each of these fonts shows up on the screen. Your average user does not know the difference between a “Menu” and a “Toolbar”.

Secondly, this font preferences screen has at least 4 different fonts on it that I can see. How’s that for ironic?

Awful System Settings

KDE seems to be trying to emulate Mac OS X and Windows Vista at the same time, but failing to emulate either of them very well. Compare this screenshot from the Mac OS X system preferences and the equivalent KDE attempt.

Mac OS X (clean, efficient, polished)
Mac OS X Settings

KDE (sloppy, inefficient, unappealing):
KDE Settings

Notice how the Mac OS X preferences screen manages to fit twice as many icons in the same amount of screen real estate, and yet, somehow the items are easier to read? That’s because the Mac uses word wrapping for long item names. Focus on this item from the KDE System Settings:

Word wrapping?

Why would you not word wrap that text? Not word wrapping that text forces all the other icons in this column to be disproportionately wide. This causes the screen to both waste space and make each column width irregular.

Also take a look at the sheer quantity of lines on the KDE System Settings screen. Lines are the enemy. They make the user interface complicated by adding more information that your brain must (subconsciously) process. The KDE System Settings has a perimeter of 3 lines. The Mac OS X System Preferences has 1.

Lasty, notice what happens when I click on “Appearance”. The screen morphs into a totally different kind of user interface. It went from a grid-shaped navigation pane to a left-hand navigation bar:

KDE Sub-Settings

The change in navigation is startling. It looks like the KDE team took a stand-alone form and crammed it into this one. In fact, I’m sure that’s what they’ve done. That’s pretty cool from a programmer’s perspective, but it makes for inconsistent UI.

Right (or was it left) Clicking

Consistency is crucial to an easy-to-use interface, especially when it comes to user input like mouse clicking. When I logged into my new Fedora 11 desktop, I noticed a tray icon in the lower left that looked like two networked computers. I right-clicked on it so I could try to turn on my wireless networking, and this is what I got:

Right click

I can’t tell what I’m supposed to do here. I right-clicked. I got a context menu, which is usual and customary for right-clicking. But nowhere do I see a place to choose my wireless network. I tried turning off the wireless networking, and turning it back on. That didn’t help.

Finally I tried a left click, and I got a different context menu (big no no):

Left click

It appears now that KDE has adopted a paradigm of providing two different context menus depending on which mouse button you click. In my view, these two context menus should be merged into a single context menu that appears when you right click. Look how Mac OS X does it. You can right click or left click and you get the same all-in-one context menu:

Right or left click

This seems so obvious and easy to get right, and it’s crucial because this isn’t the kind of feature that users will use every day. The less frequently used stuff needs to be the most obvious.

Suspend Still Does Not Work

As I was writing this post (on my Mac), my Fedora 11 laptop went to sleep and suspended. It made two ding-dong sounds before it did so. When I pressed the power button to wake it back up, I was greeted by lots of spinning fan sounds, a blinking WiFi light, and a blank screen. I had to power off the laptop to bring back a login screen.

I have been using Linux for 8 years, and only one distribution ever provided me a working suspend feature: Red Hat 7.3. Yes, the free Red Hat, back before Fedora existed. In the mean time, I’ve used Windows 95, 98, 2000, XP, and Vista, and every single one of them has provided a functioning suspend feature, and I’m no fan of Windows.

Logout

When I click the “F” menu, and then click on the “Leave” tab, the menu morphs into a list of “Leave”-like options, like shut down, restart, and logout. If I click “Restart”, I get a picture of a stretched-out moon with some text crammed next to it that says “Restart Computer”. This text doesn’t look like a button. It doesn’t act like a button when I hover on it. But guess what, it’s a button. KDE wants me to click on that button-like thing to confirm that I really want to shut down the computer. Here’s what it looks like:

Restart Confirm

When you want the user to click on a button, it needs to look and act like a button, even if you’re trying to do fancy graphics.

While I’m on the restart confirmation box, why is it so small? You have a huge screen. Spread out a bit. Get a little breathing room. Put about 50 pixels between each widget in that box, and give it about 100 pixels of padding around its perimeter.

Oh, and fix that moon. It looks like it got sat upon. :)

Chopped Text

I think this dialog speaks for itself:

Chopperoo

This was one of several dialogs that I could not read fully due to chopped text.

Icon Size Inconsistency

Icon sizes are not consistent with other icons, nor are they consistent with input elements, like the little “plus” signs in tree views. Take this example:

Plus signs

Notice how the picture of the bug is about 20 times bigger than the “plus” sign to the left of it? In my opinion, this should not be a tree view at all, but rather a nice flat list with header text between the categories. Keep it simple. Perhaps just show the quantities, and then provide a “Show details” button.

Notice also the two different styles of buttons along the bottom of the form? The bottom row uses fixed width buttons, while the next row up uses super-wide-fill-the-screen buttons. Additionally, as near as I can tell, the lower row of buttons (“OK”, “Apply”, and “Cancel”) are not actually useful. I can get what I want without them. So not only are they inconsistent, they are also superfluous.

So What Do You Actually Like?

I do like the twirly ribbon thing that appears while you are logging in to KDE. That thing is pretty cool. Smoothly animated, and yet it gives you actual progress while KDE is starting up. It is oddly engaging. I can’t take my eyes off it.

I like the new default desktop wallpaper (though I liked the Fedora 10 solar flare better).

Conclusion

In conclusion, I think that the KDE desktop as packaged by Fedora 11 needs a good, long walkthrough with a fine-toothed comb by a team of user interface experts. I encountered all the inconsistencies noted in this article within the first 15 minutes of using Fedora 11, and I’m sure a focused team could find dozens more. If they’re fixed, I think Fedora 11 and KDE show great promise for the future.

If I had more time, I would have written less

May 22nd, 2009

The prolific Mark Twain wasn’t talking about computer programmers, but he could have been.

There are a lot of differences between excellent computer programmers and your average, run-of-the-mill hackers. But there’s one major difference, and it’s subtle but critical. And, in my experience, it doesn’t have anything to do with age or tenure.

Great programmers can accomplish the same thing as run-of-the-mill hackers with a lot less code and a lot less complexity. I’m not talking about the kind of code you write for the IOCCC contest or Perl golf. I’m talking about simple, readable, minimal code that gets the job done.

It’s story time

A couple years ago, I was sitting in a room with some great programmers. Management had asked them to solve an engineering problem. The problem seemed hopelessly complicated, and most of our engineering team had put it on the back burner because it was so unapproachable. Finally management forced the problem on us, and we all sat down in a conference room and brainstormed solutions. Ideas slowly began to emerge. After 30 minutes, the engineers had produced lots of complex ideas to solve the problem: client/server approaches, database-driven approaches, roll-your-own-Linux-distro approaches, and so on. All the ideas scared us. At that point, a light turned on. Ideas started getting simpler, and simpler, and by the end of the meeting we had whittled the solution down to something more elegant, a simple Python script that Byron hacked out in a couple hours.

The point of the story is this: Great programmers produce simple solutions.

The converse of this point is that bad programmers produce huge, complicated, and expensive solutions.

It’s time for another story.

To protect the guilty, I won’t share any specifics. I’ve heard tell of a team that had produced a piece of software over the course of a few years. The software had, as usual, grown to be quite complex, and the engineering team had grown to a dozen people. The software was deployed at a few customer sites, and was reasonably successful. I talked to some of the users, and every time the story was the same: “This software is so complicated”. The users weren’t judgmental. I think they just assumed that the problem was hard to solve, so the software was hard to use, and equally hard to write. One of the most complicated parts of the software was its installation procedure. It required the user to run several scripts in a custom csh environment. The user had to become an expert in a lot of areas unrelated to the problem domain. Customers approached the team and asked them to package the software in a standard installer format for Linux. The engineers dutifully estimated how much this would cost, and came back with an estimate of nearly … are you sitting down? … a half million dollars. Why so expensive you may ask? Their software had grown so complex and so hard to maintain, that a simple change like this would cost hundreds of thousands of dollars. Customers agreed (great customers, eh?), and the team went to work. After a few months, the job was so badly done, that the installer wasn’t “standard” at all, and it totally failed.

Why does this happen?

I don’t know for sure, but I think this blog post might give a hint.