过滤实时数据时的 QSortFilterProxyModel 问题

QSortFilterProxyModel issue in filtering Real Time Data

我正在使用 qsortfilterproxymodel 从 QstandardModel 过滤数据。我能够在我的测试应用程序中实现这个过滤过程,但是当我实时使用它时[集成]它似乎没有按预期工作。

[问题]:在我的例子中,数据将每 33 毫秒(大约)写入 QstandardModel[源模型],这意味着每 1 秒连续写入 33-36 行,但是当我使用 qsortfilterproxymodel 来过滤它不显示数据,因为每个过滤器检查(指定)并且我还覆盖了 qsortfilterproxymodel 的 filterAcceptrows() 方法只是为了在使用 QMutex 过滤和释放源模型之前锁定源模型。但是我无法实现实时过滤。

[问题:] filterAcceptRows() 中添加新行时,如何只过滤添加到源模型的新行而不是整个模型 方法 ?

请帮忙..谢谢

ProxyTest.cpp

#include "proxymodeltest.h"
#include "ui_proxymodeltest.h"

QMutex mutex;
ProxyModelTest::ProxyModelTest(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::ProxyModelTest)
{
    ui->setupUi(this);

    secondtext = "SECOND";
    firstText = "FIRST";
    regExp = "\b(" + firstText + "|" + secondtext + ")\b";

    model = new QStandardItemModel();
    proxyModel = new MySortFilterProxyModel();
    proxyModel->setFilterRegExp(regExp);
    ui->tableView->setModel(proxyModel);
    proxyModel->setSourceModel(model);

    timer = new QTimer(this);
    connect(timer,SIGNAL(timeout()),this,SLOT(updateTable()));
    connect(proxyModel,SIGNAL(rowsInserted(QModelIndex,int,int)),this,
                         SLOT(checkInserted(QModelIndex,int,int)));
    timer->start(25);

}

ProxyModelTest::~ProxyModelTest()
{
    delete ui;
}

void ProxyModelTest::updateTable()
{
    static int count = 0;
    model->insertRow(0,new QStandardItem(""));
    for(int col = 0; col < 4 ;col++)
    {
        model->setItem(0,col,new QStandardItem(""));

        if( count % 2 == 0)
            model->item(0,col)->setBackground(QBrush(QColor("yellow")));
        else
            model->item(0,col)->setBackground(QBrush(QColor("lightGreen")));
    }

    if( count % 2 == 0)
    {
        model->item(0,0)->setText("FIRST");
        model->item(0,1)->setText("some text");
        model->item(0,2)->setText("some text");
        model->item(0,3)->setText("some text");
    }
    else
    {
        model->item(0,0)->setText("SECOND");
        model->item(0,1)->setText("some text");
        model->item(0,2)->setText("some text");
        model->item(0,3)->setText("some text");
    }
    count++;
}

void ProxyModelTest::on_firstCheck_toggled(bool checked)
{
    if(checked)
    {
        firstText = "FIRST";
    }
    else
    {
        firstText = "---";
    }
    regExp = "\b(" + firstText + "|" + secondtext + ")\b";
    qDebug() << regExp;
    proxyModel->setFilterRegExp(regExp);
}

void ProxyModelTest::on_checkSecond_toggled(bool checked)
{
    if(checked)
    {
        secondtext = "SECOND";
    }
    else
    {
        secondtext = "---";
    }
    regExp = "\b(" + firstText + "|" + secondtext + ")\b";
    proxyModel->setFilterRegExp(regExp);
}

void ProxyModelTest::checkInserted(QModelIndex index, int a, int b)
{
    qDebug() <<"in checkInserted";
}

MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent)
    : QSortFilterProxyModel(parent)
{
}
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow,
        const QModelIndex &sourceParent) const
{
    bool status;
    mutex.lock();
    QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
    //QModelIndex index1 = sourceModel()->index(sourceRow, 1, sourceParent);
    //QModelIndex index2 = sourceModel()->index(sourceRow, 2, sourceParent);
    qDebug() <<"in filter Accept rows";
    status = sourceModel()->data(index0).toString().contains(filterRegExp());
    mutex.unlock();
    return status;
}

void ProxyModelTest::on_pushButton_clicked()
{
    timer->stop();
}

ProxyTest.h

#ifndef PROXYMODELTEST_H
#define PROXYMODELTEST_H

#include <QMainWindow>
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include <QTimer>
#include <QDebug>
#include <QMutex>
namespace Ui {
class ProxyModelTest;
}

class MySortFilterProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT

public:
    MySortFilterProxyModel(QObject *parent = 0);

protected:
    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE;

private:
    bool dateInRange(const QDate &date) const;

};

class ProxyModelTest : public QMainWindow
{
    Q_OBJECT

public:
    explicit ProxyModelTest(QWidget *parent = 0);
    ~ProxyModelTest();

private slots:
    void updateTable();
    void on_firstCheck_toggled(bool checked);

    void on_checkSecond_toggled(bool checked);
    void checkInserted(QModelIndex index,int a,int b);

    void on_pushButton_clicked();

private:
    Ui::ProxyModelTest *ui;
    QStandardItemModel *model;
    MySortFilterProxyModel *proxyModel;
    QString firstText,secondtext,regExp;
    QTimer *timer;
};


#endif // PROXYMODELTEST_H

[更新 2]

proxyModelTest.ui

 <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>ProxyModelTest</class>
     <widget class="QMainWindow" name="ProxyModelTest">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>639</width>
        <height>399</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>ProxyModelTest</string>
      </property>
      <widget class="QWidget" name="centralWidget">
       <widget class="QTableView" name="tableView">
        <property name="geometry">
         <rect>
          <x>10</x>
          <y>30</y>
          <width>481</width>
          <height>301</height>
         </rect>
        </property>
       </widget>
       <widget class="QPushButton" name="pushButton">
        <property name="geometry">
         <rect>
          <x>550</x>
          <y>200</y>
          <width>75</width>
          <height>23</height>
         </rect>
        </property>
        <property name="text">
         <string>PushButton</string>
        </property>
       </widget>
       <widget class="QWidget" name="layoutWidget">
        <property name="geometry">
         <rect>
          <x>510</x>
          <y>60</y>
          <width>111</width>
          <height>81</height>
         </rect>
        </property>
        <layout class="QVBoxLayout" name="verticalLayout">
         <item>
          <widget class="QCheckBox" name="firstCheck">
           <property name="text">
            <string>First</string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QCheckBox" name="checkSecond">
           <property name="text">
            <string>second</string>
           </property>
          </widget>
         </item>
        </layout>
       </widget>
      </widget>
      <widget class="QMenuBar" name="menuBar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>639</width>
         <height>21</height>
        </rect>
       </property>
      </widget>
      <widget class="QToolBar" name="mainToolBar">
       <attribute name="toolBarArea">
        <enum>TopToolBarArea</enum>
       </attribute>
       <attribute name="toolBarBreak">
        <bool>false</bool>
       </attribute>
      </widget>
      <widget class="QStatusBar" name="statusBar"/>
     </widget>
     <layoutdefault spacing="6" margin="11"/>
     <resources/>
     <connections/>
    </ui>

我无法编译您的代码(缺少 ui_proxymodeltest.h),但我创建了一个可重现的示例,您可能会发现它对此有用。

我的过滤代理故意简单 - 它只传递所有五的倍数的项目:

#include <QSortFilterProxyModel>
class OneInFiveProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT
public:
    OneInFiveProxyModel(QObject *parent = nullptr);
protected:
    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE;
};

OneInFiveProxyModel::OneInFiveProxyModel(QObject *parent)
    : QSortFilterProxyModel(parent)
{
}

bool OneInFiveProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
    return sourceModel()->index(sourceRow, 0, sourceParent).data(Qt::UserRole).toInt() % 5 == 0;
}

这是一个简单的 class,它将发出我们想要放入模型中的数据:

class Ticker : public QObject
{
    Q_OBJECT
    const int count;
public:
    Ticker(int count, QObject *parent = 0);
public slots:
    void run();
signals:
    void tick(int);
    void finished();
};

#include <QThread>
Ticker::Ticker(int count, QObject *parent)
    : QObject(parent),
      count(count)
{
}

void Ticker::run() {
    for (int i = 0;  i < count;  ++i) {
        QThread::msleep(25);
        emit tick(i);
    }
    emit finished();
}

现在,让我们将它们放入应用程序中。我们将 运行 代码一次,在线程中,然后再次在它自己的线程中:

#include <QCoreApplication>
#include <QDebug>
#include <QStandardItemModel>

int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);
    QStandardItemModel model;
    OneInFiveProxyModel proxy;
    proxy.setSourceModel(&model);

    Ticker t(25);
    QObject::connect(&t, &Ticker::tick, &model, [&model](int i){
            auto item = new QStandardItem{QString::number(i)};
            item->setData(i, Qt::UserRole);
            model.appendRow(item);
        });

    // run it in this thread
    t.run();
    qDebug() << "After first run, model contains" << model.rowCount() << "items"
             << "and proxy contains" << proxy.rowCount() << "items";

    // run it again, but in a different thread
    QThread thread;
    t.moveToThread(&thread);
    QObject::connect(&thread, &QThread::started, &t, &Ticker::run);
    QObject::connect(&t, &Ticker::finished, &thread, &QThread::quit);
    QObject::connect(&thread, &QThread::finished, &app, &QCoreApplication::quit);
    thread.start();
    app.exec();  // will return when the thread finishes

    qDebug() << "After second run, model contains" << model.rowCount() << "items"
             << "and proxy contains" << proxy.rowCount() << "items";
}

运行这个,我每次在模型中获取25个item,通过代理可见5个:

After first run, model contains 25 items and proxy contains 5 items
After second run, model contains 50 items and proxy contains 10 items

几点注意事项,希望对您有所帮助:

  • 模型和代理在同一个线程中。这是必不可少的,这样代理就可以访问源模型而无需锁定
  • 我们使用信号允许我们发送要添加到源模型的数据。所以这些数据的发起者不需要与模型在同一个线程中。
  • 当我向 filterAcceptsRow 添加调试行时,我可以确认它只为每个添加的项目调用一次。