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. [SOLVED] How to make visible / select first item using a (sub)string and QSortFilterProxyModel ?
Forum Updated to NodeBB v4.3 + New Features

[SOLVED] How to make visible / select first item using a (sub)string and QSortFilterProxyModel ?

Scheduled Pinned Locked Moved General and Desktop
13 Posts 3 Posters 7.0k Views 1 Watching
  • 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.
  • M Offline
    M Offline
    mvidelgauz
    wrote on last edited by
    #1

    Hello!

    I have a list box with QSortFilterProxyModel connected to it. It is used for sorting only, not for filtering. In another line edit control of the form I am entering a (sub)string. How can I make visible (and possibly select) first row that would stay visible if I used filtering? I.e. I know that if will call setFilterFixedString with entered string than only items containing that string will become visible. But I do not want to filter items. I want them all to stay in list box but find first that matches filtering criteria and make it visible in the list (possibly select all such items)

    Thanks you!

    1 Reply Last reply
    0
    • jazzycamelJ Offline
      jazzycamelJ Offline
      jazzycamel
      wrote on last edited by
      #2

      If all you want to do is select appropriate rows then I wouldn't do this in your proxy model as its not what they are designed to do. You can get the functionality you want easily enough by using your models match() method to find the appropriate indexes and then selecting these indexes via your views selection model. The following is a simple complete example:

      widget.h
      @
      #ifndef WIDGET_H
      #define WIDGET_H

      #include <QtGui>

      class Model : public QAbstractTableModel
      {
      Q_OBJECT
      public:
      Model(QWidget *parent = 0) : QAbstractTableModel(parent){}
      int columnCount(const QModelIndex &parent) const { return 1; }
      int rowCount(const QModelIndex &parent) const { return 100; }
      QVariant data(const QModelIndex &index, int role) const {
      if(!(index.isValid() && role==Qt::DisplayRole)) return QVariant();
      return QVariant(index.row());
      }
      };

      class List : public QListView
      {
      Q_OBJECT
      public:
      List(QWidget *parent=0) : QListView(parent){}

      public slots:
      void search(QString text){
      selectionModel()->clear();
      if(text.isEmpty()) return;

          QModelIndex start=model()->index(0,0);
          QModelIndexList indexes=model()->match(start, Qt::DisplayRole, text, -1);
          foreach(QModelIndex index, indexes) selectionModel()->select(index, QItemSelectionModel::Select);
      }
      

      };

      class Widget : public QWidget
      {
      Q_OBJECT

      public:
      Widget(QWidget *parent = 0);

      private:
      Model *model;
      List *list;
      QSortFilterProxyModel *proxy;
      QLineEdit *search;
      };

      #endif // WIDGET_H
      @

      widget.cpp
      @
      #include "widget.h"

      Widget::Widget(QWidget *parent)
      : QWidget(parent)
      {
      QVBoxLayout *l=new QVBoxLayout(this);
      search=new QLineEdit(this);
      search->setPlaceholderText("Search...");
      l->addWidget(search);

      model=new Model(this);
      proxy=new QSortFilterProxyModel(this);
      proxy->setSourceModel(model);
      list=new List(this);
      list->setModel(proxy);
      connect(search, SIGNAL(textChanged(QString)), list, SLOT(search(QString)));
      l->addWidget(list);
      

      }
      @

      main.cpp
      @
      #include <QtGui/QApplication>
      #include "widget.h"

      int main(int argc, char *argv[])
      {
      QApplication a(argc, argv);
      Widget w;
      w.show();

      return a.exec&#40;&#41;;
      

      }
      @

      Model() is just a simple table model that returns a row number as data. The interesting part is the search() slot in the QListView() which is invoked by the QLineEdit()'s textChanged() signal. Typing '1' into the search box will cause all items beginning with '1' to be selected (1, 10-19). If you want to change the search behaviour for wildcarding or to use a regexp then just change the Qt::MatchFlags that are passed to the match method.

      Hope this helps ;o)

      For the avoidance of doubt:

      1. All my code samples (C++ or Python) are tested before posting
      2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
      1 Reply Last reply
      0
      • M Offline
        M Offline
        mvidelgauz
        wrote on last edited by
        #3

        Thank you jazzycamel for you prompt and detailed answer. And sorry for my late respond ((

        I will try to implement what you suggested (although I need first to translate it to PyQt ))) )

        1 Reply Last reply
        0
        • jazzycamelJ Offline
          jazzycamelJ Offline
          jazzycamel
          wrote on last edited by
          #4

          I actually prototyped this in python first as follows:

          @
          import sip
          sip.setapi('QString', 2)
          sip.setapi('QVariant', 2)

          from PyQt4.QtCore import *
          from PyQt4.QtGui import *

          class Model(QAbstractTableModel):
          def rowCount(self , parent=QModelIndex()): return 100
          def columnCount(self, parent=QModelIndex()): return 1
          def data(self, index, role=Qt.DisplayRole):
          if not (index.isValid() and role==Qt.DisplayRole): return None
          return "{0}".format(index.row())

          class Proxy(QSortFilterProxyModel): pass
          class List(QListView):
          @pyqtSlot(str)
          def search(self, text):
          self.selectionModel().clear()
          if text=="": return

              start=self.model().index(0,0)
              indexes=self.model().match(start, Qt.DisplayRole, text, hits=-1)
          
              for index in indexes:
                  self.selectionModel().select(index, QItemSelectionModel.Select)
          

          if name=="main":
          from sys import argv, exit

          class Widget(QWidget):
              def __init__(self, parent=None):
                  QWidget.__init__(self, parent)
          
                  l=QVBoxLayout(self)
          
                  self._search=QLineEdit(self, placeholderText="Search...")
                  l.addWidget(self._search)
          
                  self._model=Model(self)
                  self._proxy=Proxy(self)
                  self._proxy.setSourceModel(self._model)
                  self._list=List(self)
                  self._list.setModel(self._proxy)
                  self._search.textChanged.connect(self._list.search)
          
                  l.addWidget(self._list)
          
                  self.setFocus()
          
          a=QApplication(argv)
          w=Widget()
          w.show()
          w.raise_()
          exit(a.exec_())
          

          @

          Might save you a bit of time and effort. Hope this helps ;o)

          For the avoidance of doubt:

          1. All my code samples (C++ or Python) are tested before posting
          2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
          1 Reply Last reply
          0
          • M Offline
            M Offline
            mvidelgauz
            wrote on last edited by
            #5

            Thank you again jazzycamel for your code snippets!

            First of all, in response to your first reply I want to say that that I used proxy model for the purpose it is designed - I want my ListView to be sorted. And that also helped me to filter items of course. My original code was:
            @
            def lineEditTextEdited(self, text):
            self.listModel.setFilterFixedString(text)
            @

            'self.listModel' in my widget is QSortFilterProxyModel, I'm saving it in setModel() method of widget:
            @
            def setModel(self, model):
            self.listModel = model
            self.listView.setModel(model)
            @

            Now I am trying to use your suggestion. If I understand your code correctly in line 44 you are setting your list's model to proxy model, so in lines 22 and 23 you are calling methods index and match of proxy model. So my first version after change was:

            @
            def lineEditTextEdited(self, text):
            #self.listModel.setFilterFixedString(text)
            self.listView.selectionModel().clear()
            if text=="": return
            start=self.listModel.index(0,0)
            indexes=self.listModel.match(start, QtCore.Qt.DisplayRole, text, hits=-1)
            for index in indexes:
            self.listView.selectionModel().select(index, QItemSelectionModel.Select)
            @

            It didn't work and since I understood from your first reply that I should use original model, not proxy the second version is:

            @
            def lineEditTextEdited(self, text):
            #self.listModel.setFilterFixedString(text)
            self.listView.selectionModel().clear()
            if text=="": return
            start=self.listModel.sourceModel().index(0,0)
            indexes=self.listModel.sourceModel().match(start, QtCore.Qt.DisplayRole, text, hits=-1)
            for index in indexes:
            self.listView.selectionModel().select(index, QItemSelectionModel.Select)
            @

            In both versions 'indexes' result is empty array (0 items)...

            Can you please tell me what is wrong in my code?

            1 Reply Last reply
            0
            • jazzycamelJ Offline
              jazzycamelJ Offline
              jazzycamel
              wrote on last edited by
              #6

              In line 22-23 of my example I am calling the model() method on my view which will return the model to which that view is attached, in this case its the proxy model so your first attempt was correct.

              I'm afraid I can't tell you why this isn't working for your particular application without seeing a bit more code. What type does the data() method of your model return for the DisplayRole? I'm wondering if match is failing because its comparing search type string to something else (QVariant for example).

              Sorry I can't be more help.

              For the avoidance of doubt:

              1. All my code samples (C++ or Python) are tested before posting
              2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
              1 Reply Last reply
              0
              • M Offline
                M Offline
                mvidelgauz
                wrote on last edited by
                #7

                I think the problem is mathflags. If I am typing string from the beginning then everything is working as expected (I see that default value of 'flags' parameter of match method is Qt::MatchFlags( Qt::MatchStartsWith | Qt::MatchWrap )

                But I trying to match by substring - like setFilterFixedString(text) did. So I tried:

                @
                start=self.listModel.sourceModel().index(0,0)
                flags = Qt.MatchFlags(Qt.MatchFixedString)
                indexes=self.listModel.sourceModel().match(start, QtCore.Qt.DisplayRole, text, -1, flags)
                @

                and since you confirmed that I don't need source model:
                @
                start=self.listModel.index(0,0)
                flags = Qt.MatchFlags(Qt.MatchFixedString)
                indexes=self.listModel.match(start, QtCore.Qt.DisplayRole, text, -1, flags)
                @

                still indexes contains 0 items... and even worse - no matches even if type string from the begging..
                I suspect that I am not settings flags correctly...

                my data() method returns String for DisplayRole...

                1 Reply Last reply
                0
                • M Offline
                  M Offline
                  mvidelgauz
                  wrote on last edited by
                  #8

                  Yes, flags should be Qt.MatchFlags(Qt.MatchContains | Qt.MatchWrap)
                  (method name 'setFilterFixedString' confused me...)

                  now 'indexes' are exactly what I expect them to be

                  but self.listView.selectionModel().select(index, QItemSelectionModel.Select) has no visual effect - no items actually selected...

                  1 Reply Last reply
                  0
                  • jazzycamelJ Offline
                    jazzycamelJ Offline
                    jazzycamel
                    wrote on last edited by
                    #9

                    I was just about to suggest MatchContains ;o)

                    The indexes needs to belong to which ever model the view is attached to. If you are calling match() on the source model (which you shouldn't need to, the proxy will be fine as per example) then you will have to map them back to the proxy using mapFromSource().

                    For the avoidance of doubt:

                    1. All my code samples (C++ or Python) are tested before posting
                    2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
                    1 Reply Last reply
                    0
                    • M Offline
                      M Offline
                      mvidelgauz
                      wrote on last edited by
                      #10

                      Thank you again )))

                      no, as I mentioned in previous post I removed sourceModel(). call from code

                      'self.listModel' is what ListView is attached to

                      something is wrong with my ListView itself (regardless to code being discussed) - It doesn't select rows even when I click on them...

                      1 Reply Last reply
                      0
                      • M Offline
                        M Offline
                        mvidelgauz
                        wrote on last edited by
                        #11

                        Anyway, the original question is answered by jazzycamel

                        How do I mark this topic [SOLVED]?

                        1 Reply Last reply
                        0
                        • SGaistS Offline
                          SGaistS Offline
                          SGaist
                          Lifetime Qt Champion
                          wrote on last edited by
                          #12

                          Hi,

                          Simply edit the thread title and add [solved] to it :)

                          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
                          0
                          • M Offline
                            M Offline
                            mvidelgauz
                            wrote on last edited by
                            #13

                            Thank you SGaist! :)

                            1 Reply Last reply
                            0

                            • Login

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