[Qt-interest] Dynamic filter row from a table

david.jobet at free.fr david.jobet at free.fr
Fri Aug 21 01:57:32 CEST 2009


Hello,

one year ago I posted to that mailing list to get some info about coding a 
"filter row" in a table. (http://lists.trolltech.com/qt-interest/2008-11/thread01114-0.html)

A special row that would contain one control by column and would enable to 
search the content of the table by displaying only matching content.
e.g : If I type "ludwig" in the QLineEdit installed as filter in column "first 
name" then the table would display only the rows containing first names
containing "ludwig"

I was left with the following advice : add a custom proxy that inserts a fake 
row, then use a sortFilterProxy to filter the model according to what is set 
inside that row.

I did just that, but it adds 2 cascading proxies, the API is not very 
satisfying to me, and if you scroll, your filter row scroll with the table which is not what I want.

So I started to implement the other idea explained in the post which is to use 
setViewportMargins() to add space inside the header and put my filters there : 
in the header.

Surprinsigly, it was very easy to do, much more than creating 2 cascading 
proxies.

Find the code below, in case someone wants to do something similar.
(only the part that add the filter row, I have not implemented yet the filtering 
code itself)

I only have one question : in that code, I need to connect the table's 
scrollbar signal valueChanged() to the slot named onScroll() (as demonstrated 
in the following main)

Is there any way to do it from inside my class FilterHorizontalHeaderView 
instead of having to do it outside from the main ?

Tx

David

---
MyHeaderView.h
---

#ifndef _MY_HEADER_VIEW_
#define _MY_HEADER_VIEW_

#include <QHeaderView>
#include <QComboBox>
#include <QLineEdit>
#include <QResizeEvent>

#include <iostream>

#define VERTICAL_MARGIN 2

class FilterHorizontalHeaderView : public QHeaderView
{
  Q_OBJECT
  typedef QHeaderView inherited;

public :
  FilterHorizontalHeaderView(QWidget *parent = 0)
  : QHeaderView(Qt::Horizontal, parent)
  , column1_(new QLineEdit(this))
  , column2_(new QComboBox(this))
  , filterHeight_(0)
  {
    column1_->setText("test");
    column2_->addItem("");
    column2_->addItem("1");
    column2_->addItem("2");

    // compute once and for all the height of our filter row
    filterHeight_ = std::max(column1_->sizeHint().height(), column2_-
>sizeHint().height());

    connect(this, SIGNAL(sectionResized(int, int, int)), 
SLOT(onSectionResized_(int, int, int)));
  }

  QWidget *filterWidget(int logicalIndex)
  {
      switch (logicalIndex)
      {
        case 0 : return column1_;
        case 1 : return column2_;
        default : return 0;
      }
  }

  virtual QSize sizeHint() const
  {
    QSize inheritedSizeHint = inherited::sizeHint();
    // insert space for our filter row
    return QSize(inheritedSizeHint.width(), inheritedSizeHint.height() + 
filterHeight_ + 2 * VERTICAL_MARGIN);
  }

public slots :
  void onScroll(int value)
  {
    QRect vg = viewport()->geometry();

    // now let's position our widgets
    int start = visualIndexAt(vg.left());
    int end   = visualIndexAt(vg.right());

    start = (start == -1 ? 0           : start);
    end   = (end   == -1 ? count() - 1 : end);

    repositionFilterRow_(start, end);
  }

protected :
  virtual void updateGeometries()
  {
    QRect vg = viewport()->geometry();

    // add margins to the QHeaderView so that we reserve the remaining space 
to position our filter widgets
    setViewportMargins(0, 0, 0, filterHeight_);
    // call parent (which will recompute internal position of sections...)
    inherited::updateGeometries();

    // now let's position our widgets
    int start = visualIndexAt(vg.left());
    int end   = visualIndexAt(vg.right());

    start = (start == -1 ? 0           : start);
    end   = (end   == -1 ? count() - 1 : end);

    repositionFilterRow_(start, end);
  }

private slots :
  void onSectionResized_(int logicalIndex, int oldSize, int newSize)
  {
    // section logicalIndex has just been resized : let's reposition all 
visually available widgets on the right of it
    QRect vg  = viewport()->geometry();
    int start = visualIndex(logicalIndex); // visual index !
    int end   = visualIndexAt(vg.right());
    end       = (end == -1 ? count() - 1 : end);

    repositionFilterRow_(start, end);
  }

private :
  void repositionFilterRow_(int start, int end)
  {
    for (int i = start; i <= end; ++i)
    {
      int logical = logicalIndex(i);
      if (isSectionHidden(logical))
      {
          continue;
      }

      QWidget *fWidget = filterWidget(logical);
      if (fWidget != 0)
      {
        fWidget->move(sectionPosition(logical) - offset(), filterHeight_);
        fWidget->resize(sectionSize(logical), filterHeight_);
      }
    }
  }

private :
  QLineEdit *column1_;
  QComboBox *column2_;

  int        filterHeight_;
};

#endif // _MY_HEADER_VIEW_

---
MyModel.h
---

#ifndef _USTENSILE_UI_TEST_MY_MODEL_
#define _USTENSILE_UI_TEST_MY_MODEL_

#include <QAbstractTableModel>

#include <assert.h>

class MyModel : public QAbstractTableModel
{
public :
  // from QAbstractTableModel
  virtual int      rowCount(const QModelIndex &parent_index) const
  {
    return 3;
  }

  virtual int      columnCount(const QModelIndex &index) const
  {
    return 2;
  }

  virtual QVariant data(const QModelIndex &index, int role) const
  {
    if (role == Qt::DisplayRole)
    {
      return index.row() * columnCount(QModelIndex()) + index.column();
    }

    return QVariant();
  }

  virtual QVariant headerData(int section, Qt::Orientation orientation, int 
role = Qt::DisplayRole) const
  {
    //std::cout << "section = " << section << ", orientation = " << 
orientation << (orientation == Qt::Horizontal ? "(HORIZONTAL)":"(N/A)") << ", 
role = " << role << (role == Qt::DisplayRole ? "(DisplayRole)":"(N/A)") << 
std::endl;

    if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 
0)
    {
      static const char *names[] = {"test1", "test2"};
      assert(section >= 0);
      assert(section < (int)(sizeof(names) / sizeof(const char *)));
      return names[section];
    }

    return QVariant();
  }
};

#endif // _USTENSILE_UI_TEST_MY_MODEL_

---
main
---

int main(int argc, char **argv)
{
  QApplication app(argc, argv);
  QTableView view;
  MyModel model;
  FilterHorizontalHeaderView header;

  view.setModel(&model);
  view.setHorizontalHeader(&header);
  QObject::connect(view.horizontalScrollBar(), SIGNAL(valueChanged(int)), 
&header, SLOT(onScroll(int)));
  QObject::connect(view.verticalScrollBar(),   SIGNAL(valueChanged(int)), 
&header, SLOT(onScroll(int)));

  view.show();
  int exit_code = app.exec();

  return exit_code;
}



More information about the Qt-interest-old mailing list