PDA

View Full Version : How to filter duplicates from a proxy model.



kaushal_gaurav
28th January 2009, 14:51
Hi,

I have a proxy model created from QStandardItem Model. The proxy model has a single row.
That column contains duplicate entries. I need to populate the QlistView with this model.
but the Qlist does not need to have duplicates..


Please help..
GK

wysota
31st January 2009, 09:27
Use QSortFilterProxyModel::filterAcceptsRow() to filter out duplicate items. You'll also need to connect a custom slot to appropriate signals where you will decide which entries are duplicate so that filterAcceptsRow() can do its job later when it's called.

jfinn88
12th October 2016, 22:51
I some what understand that I need filterAcceptsRow() to filter out the duplicate items from my list model for display... but I’m not quite sure how and where to appropriate the signals.... and not sure if I have the function set up properly was hoping you could help me out....

------------proxyFilter class-------------


//---UserEvent-SortModelProxy | Constructor---//
UserListModelProxy::UserListModelProxy(QObject *parent) : QSortFilterProxyModel(parent){

}

//---Destructor---//
UserListModelProxy::~UserListModelProxy(){

}

//---FilterAcceptsRow removes dupicate enteries from userName drop-down list---//
bool UserListModelProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE
{
QModelIndex mindex = sourceModel()->index(sourceRow, 0, sourceParent);
bool validName = (sourceModel()->data(mindex, UserListModel::nameRole)).toBool();
if(validName){
return true;
}else{
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
return false;
}

//---The QModelIndex class is used to locate data in a data model | Overrides lessThan()---//
bool UserListModelProxy::lessThan(const QModelIndex &one, const QModelIndex &two) const{

//---Sort the ListView model based off the roleType---//
if(sortRole() == UserEventLog::nameRole){
QVariant nameOne = sourceModel()->data(one, UserEventLog::nameRole);
QVariant nameTwo = sourceModel()->data(two, UserEventLog::nameRole);

QString str_NameOne = nameOne.toString();
QString str_NameTwo = nameTwo.toString();

if (str_NameOne != str_NameTwo){
return str_NameOne < str_NameTwo;
}
else{
//---No Role set returns false---//
return QSortFilterProxyModel::lessThan(one, two);
}
}
else{
return false;
}
}


----------model class----------


//---userListModel Constructor---//
UserListModel::UserListModel(QObject *parent) : QAbstractListModel(parent){
//---allocates dynamic memory on heap | Instance of proxyModel class---//
m_UserListModelProxy = new UserListModelProxy(this);
m_UserListModelProxy->setSourceModel(this);
m_UserListModelProxy->setDynamicSortFilter(true);
m_UserListModelProxy->setSortCaseSensitivity(Qt::CaseInsensitive);
//m_UserListModelProxy->setSortRole(0);
}

//---userListModel Destructor---//
UserListModel::~UserListModel(){

}

void UserListModel::init(){
dbConnect();
selectEvent();
}

//---Function is called in xmui.cpp---//
void UserListModel::setContext(QQmlContext* root){

//---Sets rootContext for model to be used in userListModel QML---//
root->setContextProperty("UserListModel", this);
root->setContextProperty("UserListProxy", m_UserListModelProxy);
}

//---ProxyModel Sort setRoleName---//
void UserListModel::setSortRole (int role){
switch(role){
case 0:
//---idRole---//
if(m_UserListModelProxy->sortRole() == UserListModel::nameRole){
if(m_UserListModelProxy->sortOrder() == Qt::AscendingOrder)
m_UserListModelProxy->sort(0, Qt::DescendingOrder);
else{
m_UserListModelProxy->sort(0, Qt::AscendingOrder);
}
}
else{
m_UserListModelProxy->setSortRole(UserListModel::nameRole);
m_UserListModelProxy->sort(0);
}
break;
}
}

//---Connect to database---//
bool UserListModel::dbConnect(){
//---check if database is connected---//
if(!m_selectDataBase.isValid()){
qDebug() << "error in connecting to DB";
m_selectDataBase = QSqlDatabase::addDatabase("QSQLITE", "conn4");
m_selectDataBase.setDatabaseName(Paths::root() + "/userLog.db");

qDebug() << "database connect path: "+Paths::root()+"/userLog.db";
m_selectDataBase.open();
}
else{
qDebug() <<"connected to DB" ;
m_selectDataBase.open();
}
return m_selectDataBase.isValid();
}

int UserListModel::rowCount(const QModelIndex &parent) const{
Q_UNUSED(parent);
return m_userList.count();
}

QHash<int, QByteArray> UserListModel::roleNames() const{
QHash<int, QByteArray> roleNames;
roleNames.insert(nameRole, "userName");
return roleNames;
}

QVariant UserListModel::data(const QModelIndex &index, int role) const{
if (index.row() < 0 || index.row() >= m_userList.count()){
return QVariant();
}

QVariant text;

if(role == nameRole){
UserList userNames = m_userList.at(index.row());
text = userNames.userName;
}
return text;
}

void UserListModel::addEvent(const UserList &userName){
beginInsertRows(QModelIndex(), 0, 0);
m_userList.insert(0, userName);
endInsertRows();
}

//---Selects data from DB for display: defualt diplays all Users---//
bool UserListModel::selectEvent(){
dbConnect();
emit showBusy(true);

QSqlQuery selectQuery("SELECT userName FROM userlogevents", m_selectDataBase);
if(selectQuery.exec()){
qDebug()<<"UserEventLog::selectEvent() sql statement executed fine";
}
else{
emit xmui->alertMsg(QMessageBox::Warning, "Database Error Message 1", "Error: sql select script..."+selectQuery.lastError().text());
qDebug()<<"UserEventLog::selectEvent() Error with sql statement execution";
return selectQuery.exec();
}

UserList userNameList;
beginResetModel();
m_userList.clear();
while (selectQuery.next()){
userNameList.userName = selectQuery.value(0).toString();
addEvent(userNameList);
}
endResetModel();
emit showBusy(false);
m_selectDataBase.close();
return selectQuery.exec();
}


--------QML code--------


//---Calls dbConnect(), archiveEvent() & selectEvent() functions after QML loads---//
Component.onCompleted: {
//---calls constructors sets proxy source---//
UserEventLog.init();
//---loads userName dropDwon---//
UserListModel.init();
listView.currentIndex = 0
listView.positionViewAtBeginning();
listView.forceActiveFocus();
}

//-------------------------------------------------------------//

//---List UserNames Currently in the database---//
ComboBox {
id: userNameDropDown
implicitHeight: 40
implicitWidth: 130
KeyNavigation.tab: beginDateTextField
activeFocusOnTab: true
model: UserListModel
textRole: "userName"
style: userListCombo
Keys.onReturnPressed: {
userNameDropDown.focus = false;
beginDateTextField.focus = true;
beginDateTextField.selectAll();
}
}

anda_skoa
13th October 2016, 09:56
Question: do you need the duplicates or original sort order in a different place at the same time?
If not, you could simply sort and de-deduplicate in the model itself.

Cheers,
_

jfinn88
13th October 2016, 17:26
No I don’t need the duplicates or the original sort order...

wouldn't you need the virtual functions of the proxy to preform or override the sort() or filterAcceptsRow(), I'm not sure how to do that in the model....

jfinn88
13th October 2016, 22:08
updated code for filterAcceptsRow()



//---FilterAcceptsRow removes duplicate enteries from userName drop-down list---//
bool UserListModelProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE
{
//---Returns the index of the source model item in the model specified by the given row, column and parent index---//
QModelIndex mindex = sourceModel()->index(sourceRow, 0, sourceParent);
qDebug() << "UserListModelProxy::filterAcceptsRow mindex:" << mindex;

//---Returns the data stored under the given role for the item referred to by the index---//
bool validName = (sourceModel()->data(mindex, UserListModel::nameRole)).toBool();
qDebug() << "UserListModelProxy::filterAcceptsRow validName:" << validName;

if(validName){
bool filterRow1 = QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
qDebug() << "UserListModelProxy::filterAcceptsRow filterRow1:" << filterRow1;

//---Returns true if the item is to be included in the model---//
if(QSortFilterProxyModel::filterAcceptsRow(sourceR ow, sourceParent)){
bool filterRow2 = QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
qDebug() << "UserListModelProxy::filterAcceptsRow filterRow2:" << filterRow2;
return true;
}
else{
//---Sets the role data item for the source Model item at index to value | Returns true if successful; otherwise returns false---//
sourceModel()->setData(mindex, false, UserListModel::nameRole);
}
}
return false;
}


Added after 1 3 minutes:

Solution:

I got some help and realized I could pull out distinct records from database into model with a sql script using keyword "distinct" works great and I don't have to implement another class for filtering


QSqlQuery selectQuery("SELECT DISTINCT userName FROM userlogevents", m_selectDataBase);

although I would like to know how to properly set up and override the filterAcceptsRow() I'm just confused how to set-up the condition checking to remove duplicate entries. I know I need to set the source models index use that index to get the source models data then I need to check if the userName pulled out is unique but not sure how to do that part of the condition to return false to remove it form the model.....



//---FilterAcceptsRow removes duplicate enteries from userName drop-down list---//
bool UserListModelProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE
{
//---Sets index of the source model--//
QModelIndex mindex = sourceModel()->index(sourceRow, 0, sourceParent);

//---Sets sourceModel data | Returns data stored under the given role referred to by the index---//
bool validName = (sourceModel()->data(mindex, UserListModel::nameRole)).toBool();

//---not sure how to set up condition to remove non unique userNames from model---//

//---Check to see if data is valid---//
if(//check for unique userName){
return //boolValue;
}
else{
/* Returns true if the item in the row
* indicated by the given source_row and
* source_parent should be included in the model
*/
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
return false;
}



note: I will index my userName column in my database to try to improve search speeds

anda_skoa
14th October 2016, 10:21
No I don’t need the duplicates or the original sort order...

Then I would definitely do that in the model directly.



wouldn't you need the virtual functions of the proxy to preform or override the sort() or filterAcceptsRow(), I'm not sure how to do that in the model....

No, a proxy is only required if the model itself needs to contain all data and you need to have the sorting and filtering on top of that.
E.g. because the model can't be changed at all or the full/unsorted data is needed as well.

In a custom model, especially one that contains a copy of the data like yours, you can easily sort/filter the data directly.

But that's really off-topic for this thread.

Cheers,
_