subcommanderblog.wordpress.com

Subcommander Development Blog

Archive for February 2009

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
{
  Q_OBJECT; 

public:
  SignalTarget( QObject* src );
  ~SignalTarget(); 

  virtual void verify(); 

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

private:
  QObject* _src; 

protected:
  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 {
  public:
    Target( QObject* src ) : SignalTarget(src) {
    }

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

  Target target(_proxy);
  _proxy->reset();
  target.verify();
}
 

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()
{
  reset();
}
 

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 , ,

QAbstractItemModel again..

leave a comment »

I have implemented subcommanders working copy view as a custom QAbstractItemModel (since subcommander displays a directory tree it is a tree model). On top of the model is a proxy model based on QSortFilterProxyModel to filter working copy items based on their subversion status.

I have a mixed feeling about Qt4’s item models. It has some benefits but also a few problems.

On the positive side is the view and model separation, which of course is good. The Qt item model is really a view model and not simply a data model. But that’s not a problem, you just have to accept that there a few view things you have to handle in the model.

Another useful feature is the proxy model. As already said I use it to filter items by their working copy status and I also use it to realize part of the flat and the tree view display. Switching between both is a matter of toggling an option in the proxy model and tweaking a few options in the widget that is connected to my model.

The bad thing about the item model stuff it that it is really complicated to get it working properly. Apart from all the documentation that Qt provides on implementing custom models, it is still difficult to get all the QModelIndex stuff running.

Another issue is that it is really hard to debug. You may have a small bug in your model that crashes the application. Now debug that. :-( The issue is that you can’t directly watch the data behind a QModelIndex. So it is unfortunately very frustrating to find out what’s wrong.

From my point of view, item models are too complicated. Said that, it is what you have to use in Qt4 if the simpler model stuff doesn’t work for you. And it doesn’t work for me.

So what can I do?

A while ago I was trying to find a bug in my custom QAbstractItemModel. I couldn’t track it down with the debugger and finally started to write a couple of unit test for it using cppunit. (Yes, Qt has a unit test framework that supports signals and slot, but I’m already using ccpunit and I don’t want to have tests in different frameworks :-)

I didn’t find this bug with the tests but it helped me with another signal issue in the custom model. Under certain data constellations a change in the underlying item model wasn’t properly notified to the proxy model.

I have tried two approaches. The first checks for emitted signals from the model when inserting data into the model. The second checks the existence of index values which should exist after inserting data into the model.

I think I prefer the second approach, but I’m not yet satisfied with the test code itself. It is large and also a bit messed up…

I will describe both ways in the next articles and also write about my experience with creating a custom QAbstractProxyModel.

Written by hauner

Sunday, 1 February, 2009 at 21:36

Posted in subcommander

Tagged with ,