Mouse wheel events, event filters, and QScrollArea

— richardwb on Sunday, September 12, 2010 @ 18:21

Let’s say you have a QScrollArea which contains some custom widget. Now, although this widget itself has no use for mouse wheel events, when it’s in your QScrollArea you’d really like for it to grow and shrink in response to the wheel. Or maybe you’d just like to map vertical wheel events to horizontal scrolling, since you know the widget will never need vertical scrollbars.

The biggest problem here is that QScrollArea accepts wheel events (this isn’t strictly true, as you’ll soon see). Events propagate from the child up to the parent until someone accepts the event, and this means that QScrollArea will prevent wheel events from getting to your own code (again, this isn’t strictly true).

Reading the documentation it’s clear that you’d either want to subclass QScrollArea or use event filters. Since you don’t need to subclass QScrollArea for any other reason, using event filters sounds like a reasonable approach. You’ll set up an event filter that forces wheel events to be ignored and install it on the QScrollArea. That way, it’ll propagate up to your containing widget.

So you’d adjust your containing widget like so:

class MyWidget : public QObject
{
    public:
        // ...

        bool eventFilter(QObject* /*obj*/, QEvent* evt)
        {
            if (evt->type() == QEvent::Wheel)
            {
                // ignore the event (this effectively 
                // makes it "skip" one object)
                evt->ignore();
            }
            // return false to continue event propagation
            // for all events
            return false;
        }

    protected:
        void wheelEvent(QWheelEvent* event)
        {
            // your own custom stuff
            // ...
            // if you handle the event and don't want it to
            // propagate any further, accept it:
            event->accept();
        }
};

And then you’d use it like so:

QScrollArea* scrollArea = new QScrollArea(parent);
scrollArea->setWidget(someWidget);
// assuming that 'this' is MyWidget
scrollArea->installEventFilter(this);

This doesn’t work. Instead, what you need to do is installEventFilter() on QScrollArea’s viewport:

scrollArea->viewport()->installEventFilter(this);

In fact, QScrollArea is not the one that is accepting wheel events. It’s the viewport that is doing all of that. By installing an event filter on the viewport, the wheel events will continue to propagate up the tree.

Comments

Logitech G500 Gaming Mouse - Impressions

— richardwb on Saturday, May 22, 2010 @ 16:06

Got a Logitech G500 mouse recently and I thought I'd just quickly note my impressions of the mouse and its associated software. First, some background on my previous (non-throwaway) mice to give an idea of what I've used before:

  1. Logitech MX1000: a wireless mouse that had little to no lag in my particular environment, in contrast to:
  2. Logitech MX Revolution: the wireless mouse Logitech sent me as a replacement for the MX1000 above; had two wheels, one of which had a free spinning mode; unfortunately had terrible interference problems for me and was essentially unusable
  3. Razer DeathAdder: a wired mouse with a great shape, just wish it had more buttons
  4. Purchased the Logitech G500 because my Razer mouse was double-clicking sporadically and developed a flaky scroll wheel (scrolling down would sometimes scroll up)

I don't have much to say about the mouse itself that hasn't already been said on the internet -- it feels good in the hand and has plenty of buttons. I'm going to focus on the part I found more interesting, the on-board memory.

Now, one problem with most of these mice is that while the left, right, middle, and two side buttons (Mouse4 and Mouse5) are supported natively by Windows, any additional buttons on them require the appropriate drivers to be installed. This isn't necessarily bad in itself, but I always liked the idea of a mouse that just works, fully functional, without requiring any drivers. The DeathAdder added on-board memory to avoid this issue, so you could do things like save a macro to Mouse5 and bring it to another computer and have that macro available. Unfortunately, it only had five buttons, all of which already have standard functions, so I never found a good use for this feature. However, the on-board memory did let you save your mouse DPI settings, which was somewhat useful.

The G500 also has on-board memory, but this time it also has the spare buttons to make it worthwhile. In addition to the five standard buttons, the G500 has one extra side button, two DPI buttons, a left wheel tilt and a right wheel tilt. All of these are programmable and, as long as you stick to commands that can be sent by a keyboard or a mouse (including standard media keys like volume and playback controls), will work on any computer without drivers. It apparently does this by acting as several human interface devices, including a keyboard and a mouse:

Logitech G500 Properties Dialog Thumbnail

It's pretty interesting assigning a keystroke like 'A' to a side button or having your DPI buttons control the system's volume. After setting my bindings up I uninstalled the SetPoint software and still have a fully working mouse. Neat.

Comments

Accessing a Git repository with TortoiseHg on Windows

— richardwb on Friday, April 09, 2010 @ 10:31

If you're using Hg and you want to get hold of a Git repository there is an extension, hg-git, that lets you do this.

On Windows the installation process for this hasn't always been the easiest, as you needed to first install the dulwich Python library (which I always seemed to have trouble doing) and then hg-git. The TortoiseHg guys have made this a lot easier in recent versions of TortoiseHg, as they now include the dulwich libraries.

So, step by step:

  1. You'll need TortoiseHg 0.9.3 or later. Get the latest version.
  2. Bring up a command line and enter this line to clone the latest hg-git to your desired directory (this is "installing" hg-git). I'll use D:\TortoiseHg\hg-git in this example.

    > hg clone http://bitbucket.org/durin42/hg-git/ D:\TortoiseHg\hg-git
    
  3. Open up your user-specific Mercurial.ini. The location of this file may change occasionally, read the Mercurial docs if you have trouble finding it.
    On Windows Vista and Windows 7 this file is typically located at:

    C:\Users\YourUsername\Mercurial.ini
    

    while on Windows XP you'll find it at:

    C:\Documents and Settings\YourUsername\Mercurial.ini
    

    Alternatively, bring up the Windows "Run" dialog and enter:

    %USERPROFILE%\Mercurial.ini
    

    which will work on all recent Windows platforms.

  4. Navigate down to the [extensions] section and add this line to that section:

    hggit = D:\TortoiseHg\hg-git
    

    Where D:\TortoiseHg\hg-git is the directory you cloned hg-git to earlier. This line may already exist or may be commented out with a semicolon, in that case just remove the semicolon and update the directory.

  5. Now you can clone a Git repository like so:

    > hg clone git://github.com/richhickey/clojure.git D:\TargetDirectory
    
  6. The result is you'll have a Mercurial enabled repository at D:\TargetDirectory. You can push, pull, and generally treat it as an Hg repository. Try

    > hg help hggit
    

for additional help and information, or check the HgGit page on the Mercurial wiki.

Comments

Great Boost Documentation

— richardwb on Saturday, April 03, 2010 @ 18:20

I found out from the Life of Andrew blog that there is a new source for Boost information available. It's a Creative Commons licensed book covering a wide range of Boost libraries, some of which are usually under-represented documentation-wise (looking at you, ASIO). It was originally written in German by Boris Schäling, but Andreas Masur has translated it into English for those of us who aren't so culturally adapted.

A brief skim of the book gives an extremely promising impression and I think it'll be one of the first resources I point newcomers to Boost to in the future.

Comments

Simplify signals and slots connections with unique connections in Qt 4.6

— richardwb on Tuesday, January 12, 2010 @ 14:43

Somehow there's absolutely no mention of this in the What's New in Qt 4.6 summary, but one of my favorite new features in Qt 4.6 is a surprisingly simple one: the Qt::UniqueConnection connection type.

The default connection type for connect() is Qt::AutoConnection, which sets up either a direct or queued connection depending on whether the signaling object and receiving slot are in the same thread or not. This connection type allows you to connect a particular signal/slot pair multiple times (so emitting a signal once could call a slot twice) which has caught me on a number of occasions.

This is really simple to work around: you can add some bookkeeping to keep track of that particular connection or you can use disconnect() where appropriate to maintain 0 or 1 connections. The new Qt::UniqueConnection connection type, on the other hand, lets you be sure that a connection is made only once. It's not something you always need, but when you do, it lets you keep your code a bit cleaner.

One caveat is that Qt::UniqueConnection makes a unique Qt::AutoConnection, meaning you don't have the ability to explicitly specify a unique queued connection, for example. In a case like that you will have to keep track of the connections yourself, as before.

Comments

Qt 4.6 has prebuilt binaries for Visual Studio

— richardwb on Wednesday, December 02, 2009 @ 12:42

I've just found out that Qt 4.6 actually includes prebuilt LGPL binaries for Visual Studio 2008, so there's little reason to spend time building Qt unless you need to use some other version of Visual Studio.

The prebuilt binaries are downloadable from here. Alternatively, you can visit the Qt download page, choose LGPL, and look on the right hand side of the page for the download link.

I highly recommend using the prebuilt libraries, as you will save a tremendous amount of time (it takes hours to build Qt). After you finish downloading and installing the libraries, install the Visual Studio add-in to make it easier to handle the custom build steps for moc and ui files.

Comments

Qt 4.6 is out, compiling it with MSVC

— richardwb on Wednesday, December 02, 2009 @ 01:39

Qt 4.6 has been released (with some new features) so I've taken the opportunity to make sure my instructions on how to compile Qt with Visual Studio are still accurate. The additional steps on how to take advantage of your multi-core processor (using jom) still work, as well.

[Edit: Qt 4.6 actually has prebuilt binaries/libraries for Visual Studio 2008, which is preferable to spending hours building Qt]

I've made a note in the article but I'll repeat it here because it's a major time-saver: you should delete, from your Qt folder, these instances of mocinclude.tmp:

\Qt\2009.05\qt\src\3rdparty\webkit\WebCore\tmp\moc\debug_shared\mocinclude.tmp
\Qt\2009.05\qt\src\3rdparty\webkit\WebCore\tmp\moc\release_shared\mocinclude.tmp
\Qt\2009.05\qt\src\script\tmp\moc\debug_shared\mocinclude.tmp
\Qt\2009.05\qt\src\script\tmp\moc\release_shared\mocinclude.tmp

Deleting the first two mocinclude.tmp files will prevent a LNK2019 error while building Webkit, while deleting the last two (which are new in Qt 4.6, as far as I can tell) will prevent an Error: Unknown interface while trying to build qscriptextensionplugin.h.

Comments

An update on building Boost with MSVC (still with dual/quad/multi-core action)

— richardwb on Thursday, November 19, 2009 @ 02:05

I've noticed that in recent versions of Boost (1.39.0 and newer) there is a way to do a "Simplified Build from Source" if you're using Microsoft Visual Studio. Here are the steps as I like to write them:

  1. Download the latest version of Boost and extract it to some location (we'll assume D:\Boost\), making sure to maintain the directory structure.

  2. In a command prompt, navigate to your Boost library folder (D:\Boost\boost_1_41_0\) and then use this command, which builds your very own copy of Boost Jam (a build system):

    > bootstrap
    
  3. Now, in the same command prompt, enter this command, where N is the number of processors/cores you have available for compiling (for example, -j4 if you have a quad-core processor):

    > bjam.exe -jN
    
  4. Wait.

  5. After the build completes you will have a stage subdirectory (D:\Boost\boost_1_41_0\stage\) which will contain a lib subdirectory, containing the compiled libraries.

This simplified process removes the need to get a copy of Boost Jam, which, as trivial as it sounds, really does make a big difference to the friendliness of the entire procedure. It also reduces the number of necessary arguments to zero (or one, if you consider multi-core a necessity), although there are some situations where you may still need the older arguments (in which case I refer you to my previous entry on the subject).

Comments

SteveStreeting.com's great series of articles about distributed version control systems

— richardwb on Saturday, November 07, 2009 @ 00:43

Steve Streeting (the man behind OGRE, a 3D graphics engine) has been writing about his experiments with distributed version control systems, particularly Mercurial and Git. In particular, his latest blog post contains a summary of his experiences with the two version control systems and is probably the most in-depth article I've read on the topic.

The most interesting portions of the article (and his prior posts) are the ones which talk directly about managing the OGRE source within each of the two DVCSs. OGRE is not a small project and it's very illuminating to read about what parts of a (distributed) version control system are important to a larger project, as this is stuff you may not personally experience until it's too late to change your mind.

For my own personal projects I use Subversion (TortoiseSVN is a great piece of software) and I'm not in any particular rush to switch over to a DVCS just yet. It's simple, straightforward and fits well with my workflow. For larger projects, especially ones where you're collaborating with people across the Internet, I'd give some serious thought to using a DVCS and reading about other people's experiences is extremely helpful.

[Edit: I ended up switching about a year after this post to Mercurial for my personal projects.]

Comments

Windows Sidebar Gadgets

— richardwb on Monday, October 12, 2009 @ 11:57

Windows Vista and Windows 7 provides what they call Windows (Sidebar) Gadgets (the Sidebar moniker is removed in Windows 7). These gadgets do the same thing as similar products from Yahoo (formerly known as Konfabulator) and Apple (the Dashboard), providing small amounts of functionality and information within easy reach.

The Windows Live Gallery is a perfect example of how not to make a download center. If you take a look at the Sidebar gadgets category, most are of rather poor quality, and a fair number of the good ones use plagiarized skins and/or code.

There's no community presence on a site like that, and without a community to help deter poor behavior I think the free-for-all atmosphere makes it hard for the good submissions to float to the top. deviantART is a great example of a site that does the right stuff; their Sidebar gadgets section is cleaner and easier to use.

Anyway, I bring this up because there are some gadgets on The Hobby Lounge which you won't find on deviantART and unfortunately exist in a plagiarized state on the Windows Live Gallery. I was in the process of updating some of my older gadgets from this site and it took me a surprisingly long time to find the website again, even though I knew exactly what I was looking for. My google-fu is usually pretty decent so I figure I'll make a note of it here for myself and maybe help them get noticed a bit more.

[Edit: The Hobby Lounge is now unfortunately defunct.]

Comments

Optimization switches and Visual Studio 2008

— richardwb on Friday, October 02, 2009 @ 15:29

In a previous entry I noted that you generally do not need to micromanage your optimization switches.

However, in Visual Studio 2008 (both with and without SP1) there is a bug where the default optimization switch, /O2, is not applied even when shown in the IDE under Configuration Properties->C/C++->Optimization.

This happens when you select <inherit from parent or project defaults>. Instead, you want to explicitly set Maximize Speed (/O2) (it will show up in bold when explicitly set). You should also verify that the /O2 flag is being applied by viewing the command line (Configuration Properties->C/C++->Command Line).

There is more about this bug on Microsoft Connect, where it's also noted that this bug will be fixed in Visual Studio 2010.

Comments

Right-click context menus with Qt

— richardwb on Thursday, July 23, 2009 @ 20:47

Getting right-clicks to popup a context menu is pretty straightforward in Qt. There are just a couple of things to watch out for...

First, there are several ways to tell Qt you want a context menu. One approach is to subclass the widget and override the QWidget::contextMenuEvent() event handler. However, I think the easiest approach is to call setContextMenuPolicy(Qt::CustomContextMenu) on the widget you want, and then connect the customContextMenuRequested() signal to the appropriate slot:

// myWidget is any QWidget-derived class
myWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(myWidget, SIGNAL(customContextMenuRequested(const QPoint&)),
    this, SLOT(ShowContextMenu(const QPoint&)));

Next, note that the const QPoint& pos parameter in the customContextMenuRequested() signal is normally in widget coordinates. However, also note that classes which inherit from QAbstractScrollArea1 instead use the coordinates of their viewport(). Either way, you will need to map these coordinates to global coordinates using mapToGlobal().

Finally, simply either popup() or exec() your QMenu. Remember that popup() is non-blocking, so if you wish to use that, make sure your QMenu is created on the heap (or some other way of guaranteeing that the QMenu's lifetime outlasts the scope of the function)!

void MyClass::ShowContextMenu(const QPoint& pos) // this is a slot
{
    // for most widgets
    QPoint globalPos = myWidget->mapToGlobal(pos);
    // for QAbstractScrollArea and derived classes you would use:
    // QPoint globalPos = myWidget->viewport()->mapToGlobal(pos);

    QMenu myMenu;
    myMenu.addAction("Menu Item 1");
    // ...

    QAction* selectedItem = myMenu.exec(globalPos);
    if (selectedItem)
    {
        // something was chosen, do stuff
    }
    else
    {
        // nothing was chosen
    }
}

  1. Examples include QTreeWidget, QTreeView, QListWidget, QListView, QTableWidget, QTableView, QTextEdit, QPlainTextEdit, and QMdiArea

Comments

Consolas is a poor choice for web use

— richardwb on Monday, July 06, 2009 @ 15:22

Consolas, with ClearType enabled, is a gorgeous font, and is my preferred monospaced font. However, take a look at it without ClearType (there's a sample image on the linked Wikipedia article)---those jaggies hurt.

That's why you shouldn't define Consolas in your style sheets. You can't know/assume that everyone who views your webpage will have ClearType on (or at least some form of font-smoothing), and those who don't have it enabled will suffer through text that looks as though it were drawn freehand with a mouse.

Instead, unless you are going for a specific effect such as trying to mimic old terminals, keep your font-family for <pre> and <code> elements short and to the point. I personally use

font-family: "Lucida Console", "Monaco", "DejaVu Sans Mono", monospace;

which is usually enough to keep Courier (+ New) out of the way. If someone is missing all of these fonts they'll likely have a decent enough substitute.

Comments

Compile Qt 4.5 for Visual Studio, using your multi-core processor

— richardwb on Thursday, June 25, 2009 @ 20:28

If you want to use Qt 4.5 with Visual Studio you'll need to compile it yourself. The instructions are simple, and fairly well documented on various blogs and websites. Essentially: (read steps 7 and on for multi-threading information)

[Edit: These steps still apply for Qt 4.6, but Qt 4.6 actually has prebuilt binaries/libraries for Visual Studio 2008 which I highly suggest looking at instead]

  1. This step is important. You will potentially save some time by deleting these mocinclude.tmp files:

    \Qt\2009.05\qt\src\3rdparty\webkit\WebCore\tmp\moc\debug_shared\mocinclude.tmp
    \Qt\2009.05\qt\src\3rdparty\webkit\WebCore\tmp\moc\release_shared\mocinclude.tmp
    \Qt\2009.05\qt\src\script\tmp\moc\debug_shared\mocinclude.tmp
    \Qt\2009.05\qt\src\script\tmp\moc\release_shared\mocinclude.tmp
    

    Deleting the first two mocinclude.tmp files will prevent a LNK2019 error from building Webkit, while deleting the last two (which are present in Qt 4.6) will prevent Error: Unknown interface from occurring while trying to build qscriptextensionplugin.h. If you're missing any of them don't fret, it's actually a good thing.

  2. Using a Visual Studio Command Prompt, navigate to your Qt folder (I'll use D:\Coding\Qt\2009.05 in this example).

  3. Go to the Qt folder inside there (e.g. D:\Coding\Qt\2009.05\qt). It's the one that contains configure.exe.

  4. Enter the following command, changing win32-msvc2008 to win32-msvc2005 if necessary.

    > configure -opensource -shared -ltcg -no-qt3support -platform win32-msvc2008
    
  5. What this does is fairly self explanatory. The -shared switch will make sure that Qt is configured for dynamic linking (even though that's usually the default option there's no guarantee). We need to specify the -ltcg switch to turn on Link Time Code Generation. LTCG is off by default since, in combination with a statically built Qt, it will make linking very slow for any projects that use Qt. However, if we configure Qt for dynamic linking this won't be an issue.

  6. You can add or remove switches as desired (try configure help) but I find most of the defaults suitable. Sure, you may not need OpenGL (or SQL or Phonon or...) support right now, but it's a separate module anyway and you're already compiling the rest of Qt.

  7. All you need to do now is run nmake...but wait.

  8. Do you have a dual-core or quad-core processor or can take advantage of multi-threaded compilation? Get the jom tool instead. There's some info about jom in this Qt Labs Blogs article. You can download the latest version of jom from ftp://ftp.qt.nokia.com/jom/ (jom.zip should be just fine).

  9. Use jom by simply unzipping and running it in the same directory as configure (which is still the same as before, D:\Coding\Qt\2009.05\qt).

    > jom
    
  10. Happily build Qt much faster. For example, on my machine (a Core 2 Quad) it took an hour for the build to complete.

  11. Unless you really enjoy manually handling custom build steps, don't forget to get the Qt Visual Studio Add-In. After installing it make sure that it's aware of the location of your Qt installation (Qt->Qt Options) and then use File->New->Project... and select one of the Qt templates to get started.

Comments

#include Guidelines

— richardwb on Monday, June 22, 2009 @ 19:20

This is mostly for my own reference, but here are some of the guidelines I like to follow when it comes to #includes in C++. Given foo.cpp and foo.h:

  • foo.h should include everything it mentions and no more. For example, if you use boost::random in the implementation, don't #include <boost/random.hpp> in foo.h.

  • foo.cpp should also include everything it mentions and no more.

  • foo.cpp's first include should be #include "foo.h".

  • Precompiled headers, if used, should be specified in foo.cpp only and will need to be the first #include (#include "foo.h" will then be the second).

  • Except for the above two exceptions, the order I like to follow for #includes is:

    1. Headers from your own project (generally of the form #include "baz.h")
    2. Headers from non-standard libraries (such as Qt)
    3. Standard libraries (I include Boost in here because I use it so pervasively)
    4. Platform headers (like #include <windows.h>)
  • Within each of the above groups try to alphabetically order the #includes if at all possible.

  • Forward declare whenever you can get away with it.

Comments