Subclasssed QSortFilterProxyModel to return concatenated fields



  • I have subclassed the QSortFilterProxyModel class and I want to combine two fields.
    It's source model gets data from a database table named 'clients' with fields 'firstname' and 'lastname'.
    Can these two fields be concatenated in one in my subclassed QSortFilterProxyModel?
    Something like concat( firstname , " " , lastname).



  • Logic is wrong. Proxy models cannot map many-to-one.

    • How do you fill your source model?
    • Do you need changes made in the model to be filled back in the database?


  • Hi VRonin.
    Ifill my source model like this: model->setTable("clients"); (it 's a QSqlTableModel)

    I 'm just experimenting with QSortFilterProxyModel (and models in general) in order to become familiar (as newbee :) ).

    Look what I want to achieve:
    I have a db table.
    It's first row is empty.

    I want this table to 'feed' a QTableView and a QComboBox.

    In the table I want the first row (the empty) not to be shown.
    Also the table to be editable.
    I need changes made in the model of the table to be filled back in the database.
    And the combo 's model to be automatically updated.

    In the combo I want all the rows (and the first one) to be shown. But I also want the 2 fields concatenated.

    That's why I thought of using a proxy,
    because I 'feed' two widgets from one db table,
    I want the second model to be updated when the first changes,
    but their data, slightly, differ (by the first row).

    The first row in the db table is empty because I want the combo to have an option of 'nothing', no option, Null.
    But I don't want the table to have an empty row.
    So I thought of a model 'feeding' all the data to the combo, and a filter (this is why I thought of QSortFilterProxyModel) that removes the first row for the table.



  • the only "not morally wrong" way of doing what you are asking is using KExtraColumnsProxyModel from the KDE API (see here on how to build it), subclass it, call appendColumn() in the constructor and reimplement:

    virtual QVariant extraColumnData (const QModelIndex &parent, int row, int extraColumn, int role=Qt::DisplayRole) const override{
    if(role = Qt::DisplayRole)
    return sourceModel()->index(row,FirstNameColumn,parent).data(role).toString() + ", " + sourceModel()->index(row,LastNameColumn,parent).data(role).toString();
    return sourceModel()->index(row,FirstNameColumn,parent).data(role);
    }
    

    now you can set the combo model column to the extra column added



  • Ok, but what if I want to avoid the KDE (or any 'external'(non Qt) library)?



  • Diclaimer: This is EVIL and should not be done

    #include <QIdentityProxyModel>
    #include <QMultiHash>
    #include <QSet>
    template <class MergeType>
    class MergeDataProxy : public QIdentityProxyModel
    {
        Q_DISABLE_COPY(MergeDataProxy)
    public:
        explicit MergeDataProxy(QObject *parent=Q_NULLPTR)
            :QIdentityProxyModel(parent)
        {resetMergedRole();}
        virtual ~MergeDataProxy(){}
        void addMergedColumn(int copyFromCol, int copyToCol){
            if(copyFromCol==copyToCol) return;
            const QList<int> alreadyMerged=m_mergedColumns.values(copyToCol);
            if(alreadyMerged.contains(copyFromCol))
                return;
            m_mergedColumns.insert(copyToCol,copyFromCol);
        }
        void clearMergedColumns(){m_mergedColumns.clear();}
        const QHash<int, int>& mergedColumns() const{ return m_mergedColumns;}
        const QSet<int>& mergedRoles() const{return m_mergedRoles;}
        void setMergedRoles(const QSet<int> &mergedRoles){m_mergedRoles=mergedRoles;}
        void addMergedRole(int role){m_mergedRoles.insert(role);}
        void removeMergedRole(int role){m_mergedRoles.remove(role);}
        void clearMergedRole(){m_mergedRoles.clear();}
        void resetMergedRole(){clearMergedRole(); m_mergedRoles << Qt::DisplayRole;}
        const MergeType& separator() const{return m_separator;}
        void setSeparator(const MergeType &separator){m_separator=separator;}
        virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE{
            Q_ASSERT(index.model()==this);
            const QVariant baseData= QIdentityProxyModel::data(index,role);
            if(!m_mergedRoles.contains(role))
                return baseData;
            const QList<int> findCol = m_mergedColumns.values(index.column());
            if(findCol.isEmpty())
                return baseData;
            MergeType result  = baseData.value<MergeType>();
            foreach(int singleCol,findCol){
                result += m_separator;
                result += QIdentityProxyModel::data(QIdentityProxyModel::index(index.row(),singleCol,index.parent()),role).value<MergeType>();
            }
            return result;
        }
    private:
        QMultiHash<int,int> m_mergedColumns;
        QSet<int> m_mergedRoles;
        MergeType m_separator;
    };
    

    now you can use something like:

    MergeDataProxy<QString>* mergProx=new MergeDataProxy<QString>(this);
          mergProx->setSourceModel(model);
          mergProx->addMergedColumn(LastNameColumn,FirstNameColumn);
          mergProx->setSeparator(", ");
    


  • This disclaimer of yours really scared the hell out of me! :)
    So, what you 're really saying, is that KExtraColumnsProxyModel is the only way?



  • For what you want to do (use it in a combobox) my proxy is good but for anyone googling this in the future they must know there is no easy way of doing one-to-many or many-to-one proxy model.

    For example, in my proxy, calling addMergedRole(Qt::EditRole); and trying to edit the cell will turn your base model in a steaming pile of 💩 pretty fast


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.