Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Always select the first item in a QListView during a search

Always select the first item in a QListView during a search

Scheduled Pinned Locked Moved Solved General and Desktop
11 Posts 3 Posters 790 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • ? Offline
    ? Offline
    A Former User
    wrote on 27 Dec 2021, 19:52 last edited by
    #1

    Hi,

    I need your help. I have created a subclass of QSortFilterProxyModel with a custom filter which works very well. This proxymodel is set as model to my QListView. The code of the proxymodel looks like this:

    fungussortfilterproxymodel.h

    #ifndef FUNGUSSORTFILTERPROXYMODEL_H
    #define FUNGUSSORTFILTERPROXYMODEL_H
    
    #include <QSortFilterProxyModel>
    
    class FungusSortFilterProxyModel : public QSortFilterProxyModel
    {
        Q_OBJECT
    public:
        explicit FungusSortFilterProxyModel(QObject *parent = nullptr);
        void setIdFilter(const int &id);
        void setNameFilter(const QString &name);
        bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
    
    private:
        const int mIdColumn = 0;
        const int mNameColumn = 1;
        int mIdFilterValue = -1;
        QRegularExpression mRegularExpression;
    };
    
    #endif // FUNGUSSORTFILTERPROXYMODEL_H
    

    fungussortfilterproxymodel.cpp

    #include "fungussortfilterproxymodel.h"
    
    FungusSortFilterProxyModel::FungusSortFilterProxyModel(QObject *parent): QSortFilterProxyModel{parent} {}
    
    void FungusSortFilterProxyModel::setIdFilter(const int &id)
    {
        if(mIdFilterValue != id)
        {
            mIdFilterValue = id;
            emit invalidateFilter();
        }
    }
    
    void FungusSortFilterProxyModel::setNameFilter(const QString &name)
    {
        mRegularExpression.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
        if(name.isEmpty()){
            mRegularExpression.setPattern(QString());
        } else if (name.contains(QRegularExpression("\\s"))) {
            QStringList word = name.split(QRegularExpression("\\s"));
            mRegularExpression.setPattern("^" + QRegularExpression::escape(word[0]) + "\\w*\\s" + QRegularExpression::escape(word[1]) + "\\w*");
        } else {
            mRegularExpression.setPattern("^" + QRegularExpression::escape(name) + "\\w*");
        }
    
        emit invalidateRowsFilter();
    }
    
    bool FungusSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
    {
        if(!mRegularExpression.pattern().isEmpty()){
            const QModelIndex index = sourceModel()->index(source_row, mNameColumn, source_parent);
            const QString name = sourceModel()->data(index).toString();
            return (name.contains(mRegularExpression));
        }
        if(mIdFilterValue != -1){
            const int id = sourceModel()->index(source_row, mIdColumn).data().toInt();
            return (id == mIdFilterValue);
        } else {
            return true;
        }
    }
    

    In the next step, I want to highlight the first Item of QListView at programm start and during search via a QLineEdit. So I make a slot and connect the signal textChanged to my custom slot in the constructor of mainwindow.cpp.

    connect(ui->searchbar, &QLineEdit::textChanged, this, &MainWindow::handleItemSelection);
    

    My custom slot, called handleItemSelection looks like this:

    void MainWindow::handleItemSelection(const QString &name)
    {
        Q_UNUSED(name);
    
        QModelIndex index;
    
        if(ui->modelSelection->currentIndex() == Model::Fullnames){
            index = mFungusList->model()->index(0,1);
            mFungusList->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
        }
    }
    

    This works, too. So I connect the signal QItemSelectionModel::selectionChanged to my custom slot.

    connect(mFungusList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::fungusSelectionChanged);
    

    fungusSelectionChanged slot:

    void MainWindow::fungusSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
    {
        QModelIndex index = selected.indexes().first();
    
        const int id = mFungusProxyModel->data(index.siblingAtColumn(0)).toInt();
        const QString redlist = mFungusProxyModel->data(index.siblingAtColumn(7)).toString();
    
        if(redlist.isEmpty())
            ui->redlist->setCurrentIndex(-1);
    
        mSynonymFilterModel->setIdFilter(id);
        index = mFungusProxyModel->mapToSource(index);
        mMapper->setCurrentModelIndex(index);
    }
    

    And this works too if I click the item in QListView with my mouse. But when I use the QLineEdit (search function for QListView) my programm crash at FungusSortFilterProxyModel at the position "emit invalidateRowsFilter()" and says:

    error.png

    In my qDebug() output qt creator says:
    ASSERT: "!isEmpty()" in file /usr/include/qt6/QtCore/qlist.h, line 591

    I have also figured out that it must have something to do with this line. If I remove this line, the search works and my program does not crash, but I do not see the error.

    QModelIndex index = selected.indexes().first();
    

    However, if I omit this line then my QDataWidgetMapper (mMapper) no longer works....

    Can someone please help me and tell me what I'm doing wrong?

    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 27 Dec 2021, 20:00 last edited by
      #2

      Hi,

      From the looks of it, you do not take into account the empty selection case.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      ? 1 Reply Last reply 27 Dec 2021, 20:11
      1
      • S SGaist
        27 Dec 2021, 20:00

        Hi,

        From the looks of it, you do not take into account the empty selection case.

        ? Offline
        ? Offline
        A Former User
        wrote on 27 Dec 2021, 20:11 last edited by
        #3

        @SGaist

        Can you explain in more detail where I need to put this? Or do you mean in the FungusSortFilterProxyModel::setNameFilter(const QString &name)?

        1 Reply Last reply
        0
        • M Offline
          M Offline
          mpergand
          wrote on 27 Dec 2021, 20:29 last edited by mpergand
          #4

          @Gabber said in Always select the first item in a QListView during a search:

          selected.indexes().first();

          if(selected.indexes().isEmpy())
          {
          // no selection at all
          }
          else
          {
          QModelIndex index = selected.indexes().first();
          }
          
          ? 1 Reply Last reply 27 Dec 2021, 20:34
          1
          • M mpergand
            27 Dec 2021, 20:29

            @Gabber said in Always select the first item in a QListView during a search:

            selected.indexes().first();

            if(selected.indexes().isEmpy())
            {
            // no selection at all
            }
            else
            {
            QModelIndex index = selected.indexes().first();
            }
            
            ? Offline
            ? Offline
            A Former User
            wrote on 27 Dec 2021, 20:34 last edited by
            #5

            @mpergand
            I am so ....! This was the solution! Thanks
            Now I do:

                QModelIndex index;
                if(selected.indexes().isEmpty()){
                    return;
                } else {
                   index = selected.indexes().first();
                }
            
            1 Reply Last reply
            0
            • ? Offline
              ? Offline
              A Former User
              wrote on 28 Dec 2021, 12:50 last edited by
              #6

              Something is still strange, maybe someone can give me a hint. In my MainWindow I have a QStackWidget with 3 QListViews. The three QListViews are selected via a QComboBox. I connect it in my mainwindow.cpp via:

                  connect(ui->modelSelection, QOverload<int>::of(&QComboBox::currentIndexChanged), ui->stackedListViews, &QStackedWidget::setCurrentIndex);
                  connect(ui->searchbar, &QLineEdit::textChanged, this, &MainWindow::handleItemSelection);
                  connect(ui->modelSelection, &QComboBox::currentTextChanged, this, &MainWindow::handleItemSelection);
                  connect(mFungusList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::fungusSelectionChanged);
                  connect(mSynonymList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::synonymSelectionChanged);
              

              Yesterday I wrote that it works fine with my first QListView (index 0 of the QComboBox). But as soon as it selects the second QListView via the QComboBox, the search works, but it doesn't map me the data via the QDataWidgetMapper (mMapper) anymore. I even created a separate slot.

              void MainWindow::synonymSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
              {
                  QModelIndex selectedIndex;
                  QModelIndex proxyIndex;
                  QModelIndex sourceIndex;
              
                  if(selected.indexes().isEmpty()){
                      return;
                  } else {
                     selectedIndex = selected.indexes().first();
                  }
              
                  const int id = mSynonymProxyModel->data(selectedIndex.siblingAtColumn(2)).toInt();
                  qDebug() << id;
                  mFungusProxyModel->setIdFilter(id);
                  mSynonymFilterModel->setIdFilter(id);
              
                  proxyIndex = mFungusProxyModel->index(0,0);
                  sourceIndex = mFungusProxyModel->mapToSource(proxyIndex);
              
                  qDebug() << sourceIndex;
              
                  const QString redlist = mFungusProxyModel->data(proxyIndex.siblingAtColumn(7)).toString();
              
                  qDebug() << redlist;
              
                  if(redlist.isEmpty())
                      ui->redlist->setCurrentIndex(-1);
              
                  mMapper->setCurrentModelIndex(sourceIndex);
                  mFungusProxyModel->setIdFilter(-1);
              
              }
              

              My complete handleItemSelection method looks like this:

              void MainWindow::handleItemSelection(const QString &name)
              {
                  Q_UNUSED(name);
              
                  QModelIndex index;
              
                  if(ui->modelSelection->currentIndex() == Model::Fullnames){
                      index = mFungusList->model()->index(0,1);
                      mFungusList->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
                  }
                  if(ui->modelSelection->currentIndex() == Model::Synonyms){
                      index = mSynonymList->model()->index(0,1);
                      mSynonymList->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
                  }
                  if(ui->modelSelection->currentIndex() == Model::GermanNames){
                      index = mGermanNameList->model()->index(0,0);
                      mGermanNameList->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
                  }
              }
              

              When I switch to synonyms in my QComboBox and select an entry via mouse click from the QListView it works as expected, when I try to select an entry via the search it does not recognize the data for me. My proxyIndex is then always QModelIndex(-1,-1,0x0,QObject(0x0)).

              I have no idea what could be wrong. Does anyone have a tip for me again?

              M 1 Reply Last reply 28 Dec 2021, 14:04
              0
              • ? A Former User
                28 Dec 2021, 12:50

                Something is still strange, maybe someone can give me a hint. In my MainWindow I have a QStackWidget with 3 QListViews. The three QListViews are selected via a QComboBox. I connect it in my mainwindow.cpp via:

                    connect(ui->modelSelection, QOverload<int>::of(&QComboBox::currentIndexChanged), ui->stackedListViews, &QStackedWidget::setCurrentIndex);
                    connect(ui->searchbar, &QLineEdit::textChanged, this, &MainWindow::handleItemSelection);
                    connect(ui->modelSelection, &QComboBox::currentTextChanged, this, &MainWindow::handleItemSelection);
                    connect(mFungusList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::fungusSelectionChanged);
                    connect(mSynonymList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::synonymSelectionChanged);
                

                Yesterday I wrote that it works fine with my first QListView (index 0 of the QComboBox). But as soon as it selects the second QListView via the QComboBox, the search works, but it doesn't map me the data via the QDataWidgetMapper (mMapper) anymore. I even created a separate slot.

                void MainWindow::synonymSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
                {
                    QModelIndex selectedIndex;
                    QModelIndex proxyIndex;
                    QModelIndex sourceIndex;
                
                    if(selected.indexes().isEmpty()){
                        return;
                    } else {
                       selectedIndex = selected.indexes().first();
                    }
                
                    const int id = mSynonymProxyModel->data(selectedIndex.siblingAtColumn(2)).toInt();
                    qDebug() << id;
                    mFungusProxyModel->setIdFilter(id);
                    mSynonymFilterModel->setIdFilter(id);
                
                    proxyIndex = mFungusProxyModel->index(0,0);
                    sourceIndex = mFungusProxyModel->mapToSource(proxyIndex);
                
                    qDebug() << sourceIndex;
                
                    const QString redlist = mFungusProxyModel->data(proxyIndex.siblingAtColumn(7)).toString();
                
                    qDebug() << redlist;
                
                    if(redlist.isEmpty())
                        ui->redlist->setCurrentIndex(-1);
                
                    mMapper->setCurrentModelIndex(sourceIndex);
                    mFungusProxyModel->setIdFilter(-1);
                
                }
                

                My complete handleItemSelection method looks like this:

                void MainWindow::handleItemSelection(const QString &name)
                {
                    Q_UNUSED(name);
                
                    QModelIndex index;
                
                    if(ui->modelSelection->currentIndex() == Model::Fullnames){
                        index = mFungusList->model()->index(0,1);
                        mFungusList->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
                    }
                    if(ui->modelSelection->currentIndex() == Model::Synonyms){
                        index = mSynonymList->model()->index(0,1);
                        mSynonymList->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
                    }
                    if(ui->modelSelection->currentIndex() == Model::GermanNames){
                        index = mGermanNameList->model()->index(0,0);
                        mGermanNameList->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
                    }
                }
                

                When I switch to synonyms in my QComboBox and select an entry via mouse click from the QListView it works as expected, when I try to select an entry via the search it does not recognize the data for me. My proxyIndex is then always QModelIndex(-1,-1,0x0,QObject(0x0)).

                I have no idea what could be wrong. Does anyone have a tip for me again?

                M Offline
                M Offline
                mpergand
                wrote on 28 Dec 2021, 14:04 last edited by
                #7

                @Gabber said in Always select the first item in a QListView during a search:

                when I try to select an entry via the search it does not recognize the data for me. My proxyIndex is then always QModelIndex(-1,-1,0x0,QObject(0x0)).

                I'm not sure to understand what " to select an entry via the search" really means.

                QModelIndex(-1,-1,0x0,QObject(0x0)).
                Obviously, there's no selection, so you have to select one item by code I guess.

                ? 1 Reply Last reply 28 Dec 2021, 14:35
                0
                • M mpergand
                  28 Dec 2021, 14:04

                  @Gabber said in Always select the first item in a QListView during a search:

                  when I try to select an entry via the search it does not recognize the data for me. My proxyIndex is then always QModelIndex(-1,-1,0x0,QObject(0x0)).

                  I'm not sure to understand what " to select an entry via the search" really means.

                  QModelIndex(-1,-1,0x0,QObject(0x0)).
                  Obviously, there's no selection, so you have to select one item by code I guess.

                  ? Offline
                  ? Offline
                  A Former User
                  wrote on 28 Dec 2021, 14:35 last edited by
                  #8

                  @mpergand said in Always select the first item in a QListView during a search:

                  I'm not sure to understand what " to select an entry via the search" really means.

                  I have a QLineEdit that searches my data in my subclass QSortFilterProxyModel (in my case FungusSortFilterProxyModel) via the setNameFilter(QString) function using a Regular Expression. And after each search I want to select the first element in my second QListView (mSynonymList) and map the data via QDataWidgetmapper to the corresponding QLineEdits.

                  If I click it with the mouse everything works as expected, but if I tell it to select it (see handleItemSelection) unfortunately it doesn't work. But it is not clear to me why not....

                  I hope I could explain it a little bit...

                  M 1 Reply Last reply 28 Dec 2021, 14:59
                  0
                  • ? A Former User
                    28 Dec 2021, 14:35

                    @mpergand said in Always select the first item in a QListView during a search:

                    I'm not sure to understand what " to select an entry via the search" really means.

                    I have a QLineEdit that searches my data in my subclass QSortFilterProxyModel (in my case FungusSortFilterProxyModel) via the setNameFilter(QString) function using a Regular Expression. And after each search I want to select the first element in my second QListView (mSynonymList) and map the data via QDataWidgetmapper to the corresponding QLineEdits.

                    If I click it with the mouse everything works as expected, but if I tell it to select it (see handleItemSelection) unfortunately it doesn't work. But it is not clear to me why not....

                    I hope I could explain it a little bit...

                    M Offline
                    M Offline
                    mpergand
                    wrote on 28 Dec 2021, 14:59 last edited by mpergand
                    #9

                    @Gabber
                    Put a breakpoint at the beginning of handleItemSelection(),
                    run the debugger and step by step look what happens here.

                    1 Reply Last reply
                    1
                    • ? Offline
                      ? Offline
                      A Former User
                      wrote on 28 Dec 2021, 22:42 last edited by
                      #10

                      I think I have found my error.

                          //3 QListViews with 3 subclass QSortFilterProxyModel
                          mFungusList->setModel(mFungusProxyModel);
                          mSynonymList->setModel(mSynonymProxyModel);
                          mGermanNameList->setModel(mGermanNameProxyModel);
                      

                      I already said that I have my 3 QListViews in a QStack widget. If I now search the first ProxyModel (in my case mFungusProxyModel) and have no hit then select the second page of my QStackWidget and search the stored model (in my case mSynonynProxyModel). Since I have the following signal

                      connect(mSynonymList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::synonymSelectionChanged);
                      

                      it accesses the slot SynonymSelectionChanged and exactly here it uses mFungusProxyModel which is "empty" since no hit was found and therefore I always get an empty QModelIndex.

                      I simply created a second model in my Constructor of MainWindow.cpp and added the source model twice.

                          mFungusProxyModel = new FungusSortFilterProxyModel;
                          mFungusFilterModel = new FungusSortFilterProxyModel;
                      
                          // add source Model
                          mFungusProxyModel->setSourceModel(mFungusDataModel);
                          mFungusFilterModel->setSourceModel(mFungusDataModel);
                      

                      @SGaist @mpergand
                      Now I just have a question for the experts, can you do something like that or can it cause complications?

                      It is currently working.

                      M 1 Reply Last reply 28 Dec 2021, 23:06
                      0
                      • ? A Former User
                        28 Dec 2021, 22:42

                        I think I have found my error.

                            //3 QListViews with 3 subclass QSortFilterProxyModel
                            mFungusList->setModel(mFungusProxyModel);
                            mSynonymList->setModel(mSynonymProxyModel);
                            mGermanNameList->setModel(mGermanNameProxyModel);
                        

                        I already said that I have my 3 QListViews in a QStack widget. If I now search the first ProxyModel (in my case mFungusProxyModel) and have no hit then select the second page of my QStackWidget and search the stored model (in my case mSynonynProxyModel). Since I have the following signal

                        connect(mSynonymList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::synonymSelectionChanged);
                        

                        it accesses the slot SynonymSelectionChanged and exactly here it uses mFungusProxyModel which is "empty" since no hit was found and therefore I always get an empty QModelIndex.

                        I simply created a second model in my Constructor of MainWindow.cpp and added the source model twice.

                            mFungusProxyModel = new FungusSortFilterProxyModel;
                            mFungusFilterModel = new FungusSortFilterProxyModel;
                        
                            // add source Model
                            mFungusProxyModel->setSourceModel(mFungusDataModel);
                            mFungusFilterModel->setSourceModel(mFungusDataModel);
                        

                        @SGaist @mpergand
                        Now I just have a question for the experts, can you do something like that or can it cause complications?

                        It is currently working.

                        M Offline
                        M Offline
                        mpergand
                        wrote on 28 Dec 2021, 23:06 last edited by mpergand
                        #11

                        @Gabber said in Always select the first item in a QListView during a search:

                        // add source Model
                        mFungusProxyModel->setSourceModel(mFungusDataModel);
                        mFungusFilterModel->setSourceModel(mFungusDataModel);

                        Seems strange to me, but maybe i'm wrong - Model-View pattern gives me headache, too complicated for my little brain :)

                        In one of my app, i'm using a sorting model and i do like this:

                        historyModel=new HistoryModel();
                        historySortFilter= new HistorySortFilter();
                        
                        historySortFilter->setSourceModel(historyModel);
                        tableView->setModel(historySortFilter);
                        

                        As you can see, i first set the proxy model to the sorting model,
                        then set the sorting model to the view.

                        Hope this helps.

                        1 Reply Last reply
                        0

                        8/11

                        28 Dec 2021, 14:35

                        • Login

                        • Login or register to search.
                        8 out of 11
                        • First post
                          8/11
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved