【问题标题】:QSortFilterProxyModel issue in filtering Real Time Data过滤实时数据中的 QSortFilterProxyModel 问题
【发布时间】:2016-09-11 04:04:57
【问题描述】:

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

[问题]:在我的情况下,数据将每 33 毫秒(大约)写入 QstandardModel[sourcemodel],这意味着每 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>

【问题讨论】:

  • 显示你的代码,没有细节很难说。 “我还重写了 qsortfilterproxymodel 的 filterAcceptrows() 方法,只是为了在过滤之前锁定源模型” - 不确定你在锁定方面到底在做什么,但这听起来不安全,因为 filterAcceptRows() 不会是唯一访问需要同步的源模型。
  • 展示你的代码,更好的是,一个 MCVE (stackoverflow.com/help/mcve)
  • 很抱歉提供了更多信息,因为我不知道格式化代码所以花了一些时间:-( ..@FrankOsterfeld 我已经编辑了我的问题并添加了代码。将来 updateTable( ) 将被移动到一个线程。
  • 您的示例不完整(它缺少Ui::ProxyModelTest 的定义),而且远非最小。我建议你做一个小例子,过滤一些简单的东西(例如,接受整数值的交替行),只输出到QDebug,而不是涉及 GUI。另外,我不明白你为什么在那里有一个互斥锁,因为这里的一切都在主线程中。
  • 嗨@TobySpeight .. 在集成项目中,我们将 updateModel() 移动到一个线程,即在 while(1) 内的某种 run() 中定义。所以我添加了一个互斥锁。此时我只需要知道如何过滤新添加的行,而不是再次过滤整个模型(sourceModel)。例如:如果当前 rowCount 为 20,那么如果我添加第 21 行,那么它会调用 filterAcceptsRow 20 次。所以如果我以某种方式过滤最新的,那将是完美的。想想如果有 2000 行并且 fliter acceptrows 被调用 2000 次,只是为了过滤每 25 毫秒将添加新行的位置。请提出建议。

标签: c++ qt qtableview qsortfilterproxymodel


【解决方案1】:

我无法编译您的代码(缺少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 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 个项目,通过代理可以看到 5 个:

第一次运行后,模型包含 25 个项目,代理包含 5 个项目
第二次运行后,模型包含 50 个项目,代理包含 10 个项目

需要注意的几点,可能对您有所帮助:

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

【讨论】:

  • 嗨@Toby Speight,感谢您的帮助。我已经上传了我错过的proxymodeltest.ui,请运行它,并在filterAcceptRows 中启用调试行,您可以看到问题。我正在根据您的建议更改代码,并感谢您的意见,请让我知道您对此的看法。
猜你喜欢
  • 1970-01-01
  • 2015-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-13
  • 2015-07-01
  • 1970-01-01
  • 2020-09-07
相关资源
最近更新 更多