Subcommander Development Blog

creating a custom QAbstractProxyModel

leave a comment »

Part I, getting started.

I’m currently implementing a custom QAbstractProxyModel for subcommanders working copy view. The first reason is that I want to improve the status filtering in subcommander which is not possible with the current code based on QSortFilterProxyModel. The second reason is to get full control of sorting. My attempt to add sorting to the current code failed in such a frustrating way that I don’t want to track down the problem and simply start again with fresh code that I will be able to debug :-)

Before starting with the interesting stuff (filtering and sorting) I will create a simple proxy model which does not do any sorting or filtering. It will just pass through the original (tree) model without modification. Getting this running is a big step toward the final goal.

Important for a proxy model is that is emits all the signals the source or original model emits. To achieve this the proxy model must connect to all (interesting) signals from the source model and emit the same signals with proper information from the proxy model.

I have created a test checking two signals emitted from the proxy model. To count the emitted signals I’m using a SignalTarget class which connects to the proxy signals and checks if the signals were emitted. Since the verification (CPPUNIT_ASSERT) is done inside this class it is a mock. It replaces the widget (in my case a QTreeView) that will be connected to the proxy model in the production code.

class  SignalTarget : public  QObject

  SignalTarget( QObject* src );

  virtual void verify(); 

public  slots:
  void modelAboutToBeReset();
  void modelReset(); 

  QObject* _src; 

  int  _hitModelAboutToBeReset;
  int  _hitModelReset;

The idea is to count the emitted signals and check the results in the verify method. It is not yet complete. It does handle only a subset of the signals we need to check. I have declared verify as virtual so it easy to re-implement and do the actual check. The default implementation is empty. The constructor and destructor will simply connect and disconnect all signals the we need to handle in the proxy model. The slot method names correspond to the model signals.

A test method can now use SignalTarget to check the signals from the proxy model. You can see the complete test below. _proxy is the proxy model and gets created by the test setup. The reset() method triggers both signals. Because I care only for the reset signals I have overwritten verify() to simply check their call counts.

void WcViewItemProxyModelTest::emitsModelResetSignals()
  class Target : public SignalTarget {
    Target( QObject* src ) : SignalTarget(src) {

    void verify() {
      CPPUNIT_ASSERT_EQUAL( 1, _hitModelAboutToBeReset );
      CPPUNIT_ASSERT_EQUAL( 1, _hitModelReset );

  Target target(_proxy);

This is similar to the signal testing I mentioned in my last article. The only difference is that originally I also tried to check QModelIndex indices which didn’t work to well. The problem was creating valid expected indices to assert on. I tried to remember indices from previous signals and build new expected indices (sibling, parent,child) from them. Unfortunately this approach got complicated rather fast so you couldn’t follow the code anymore.

To get the test running I simply connect the soure model modelReset signal to the srcModelReset() slot in the proxy model and call reset(). reset() will emit both signals the test wants to see.

void WcViewItemProxyModel::srcModelReset()

Very simple so far, but one has to start somewhere. :-) I guess it will get more interesting for some of the other signals the proxy model has to handle. More on this in the next article.


Written by hauner

Sunday, 8 February, 2009 at 19:07

Posted in subcommander

Tagged with , ,

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: