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. Using a custom model with QCompleter and QLineEdit
QtWS25 Last Chance

Using a custom model with QCompleter and QLineEdit

Scheduled Pinned Locked Moved Unsolved General and Desktop
5 Posts 2 Posters 2.2k 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.
  • T Offline
    T Offline
    TheFlyingMooseMan
    wrote on last edited by
    #1

    Within my app I have been using a QSqlQueryModel. I want to replace this with my own model (inherited from QAbstractTableModel), as I want to add additional items to the model that drives QCompleter. I could do this in SQL, it'll be much slower. I also want to be able to remove/replace/add items to this model.
    I started with the following definition of my own model class. However, the completer does not show a dropdown box when text is entered in my QLineEdit:

    class SearchItemModel : public QAbstractTableModel
    {
        Q_OBJECT
    
    public:
        SearchItemModel();
    
        int rowCount(const QModelIndex &parent) const;
        Qt::ItemFlags flags(const QModelIndex &index) const { Q_UNUSED(index); return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren; }
        int columnCount(const QModelIndex &parent) const;
        QVariant data(const QModelIndex &index, int role) const;
    
    
    public slots:
        void populate();
    
    private:
        typedef struct Tuple
        {
            SBIDBase::sb_type itemType;
            int               songID;
            QString           songTitle;
            int 	          performerID;
            QString           performerName;
            int               albumID;
            QString           albumTitle;
            QString           display;
        } Tuple;
    
        QList<Tuple> _searchItems;
    
        void _init();
    };
    

    The completer is constructed as follows:

        QCompleter* c=new QCompleter();
        c->setModel(qtm);
        c->setCaseSensitivity(Qt::CaseInsensitive);
        c->setModelSorting(QCompleter::CaseSensitivelySortedModel);
        c->setFilterMode(Qt::MatchStartsWith);
        c->setCompletionMode(QCompleter::PopupCompletion);
        c->setCompletionColumn(0);
    

    This works perfectly with the QSqlQueryModel, but not as well with my own model class. Do I need any other method implemented in my model?

    Any hint or reply is greatly appreciated!

    Thanks,
    Roy

    raven-worxR 1 Reply Last reply
    0
    • T TheFlyingMooseMan

      Within my app I have been using a QSqlQueryModel. I want to replace this with my own model (inherited from QAbstractTableModel), as I want to add additional items to the model that drives QCompleter. I could do this in SQL, it'll be much slower. I also want to be able to remove/replace/add items to this model.
      I started with the following definition of my own model class. However, the completer does not show a dropdown box when text is entered in my QLineEdit:

      class SearchItemModel : public QAbstractTableModel
      {
          Q_OBJECT
      
      public:
          SearchItemModel();
      
          int rowCount(const QModelIndex &parent) const;
          Qt::ItemFlags flags(const QModelIndex &index) const { Q_UNUSED(index); return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren; }
          int columnCount(const QModelIndex &parent) const;
          QVariant data(const QModelIndex &index, int role) const;
      
      
      public slots:
          void populate();
      
      private:
          typedef struct Tuple
          {
              SBIDBase::sb_type itemType;
              int               songID;
              QString           songTitle;
              int 	          performerID;
              QString           performerName;
              int               albumID;
              QString           albumTitle;
              QString           display;
          } Tuple;
      
          QList<Tuple> _searchItems;
      
          void _init();
      };
      

      The completer is constructed as follows:

          QCompleter* c=new QCompleter();
          c->setModel(qtm);
          c->setCaseSensitivity(Qt::CaseInsensitive);
          c->setModelSorting(QCompleter::CaseSensitivelySortedModel);
          c->setFilterMode(Qt::MatchStartsWith);
          c->setCompletionMode(QCompleter::PopupCompletion);
          c->setCompletionColumn(0);
      

      This works perfectly with the QSqlQueryModel, but not as well with my own model class. Do I need any other method implemented in my model?

      Any hint or reply is greatly appreciated!

      Thanks,
      Roy

      raven-worxR Offline
      raven-worxR Offline
      raven-worx
      Moderators
      wrote on last edited by
      #2

      @TheFlyingMooseMan
      you should rather show the implementation of your SearchItemModel class than the definition.

      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
      If you have a question please use the forum so others can benefit from the solution in the future

      1 Reply Last reply
      1
      • T Offline
        T Offline
        TheFlyingMooseMan
        wrote on last edited by
        #3

        Here it is:

        #include "SearchItemModel.h"
        
        #include "Context.h"
        #include "DataAccessLayer.h"
        
        SearchItemModel::SearchItemModel()
        {
        }
        
        int
        SearchItemModel::rowCount(const QModelIndex &parent) const
        {
            Q_UNUSED(parent);
            return _searchItems.count();
        }
        
        int
        SearchItemModel::columnCount(const QModelIndex &parent) const
        {
            Q_UNUSED(parent);
            return 3;
        }
        
        QVariant
        SearchItemModel::data(const QModelIndex &index, int role) const
        {
            Q_UNUSED(role);
            if(_searchItems.count()==0 || !index.isValid())
            {
                return QVariant();
            }
        
            if(index.row()<_searchItems.count())
            {
                Tuple t=_searchItems.at(index.row());
                switch(index.column())
                {
                case 0:
                    return t.display;
                    break;
        
                case 1:		// 	itemid
                    int itemID;
                    switch(t.itemType)
                    {
                        case SBIDBase::sb_type_song:
                            itemID=t.songID;
                            break;
        
                        case SBIDBase::sb_type_performer:
                            itemID=t.performerID;
                            break;
        
                        case SBIDBase::sb_type_album:
                            itemID=t.albumID;
                            break;
        
                        default:
                            itemID=-1;
                    }
                    return itemID;
                    break;
        
                case 2:		//	itemtype
                    return t.itemType;
                    break;
                }
            }
            return QVariant();
        }
        
        
        ///	Slots
        void
        SearchItemModel::populate()
        {
            QString query=QString
            (
                "SELECT  "
                    "t.type, "
                    "t.song_id, "
                    "t.title, "
                    "t.artist_id, "
                    "t.name, "
                    "t.record_id, "
                    "t.title "
                "FROM "
                    "table t "
            )
                .arg(SBIDBase::sb_type_song)
                .arg(SBIDBase::sb_type_album)
                .arg(SBIDBase::sb_type_performer)
            ;
        
            DataAccessLayer* dal=Context::instance()->getDataAccessLayer();
            QSqlDatabase db=QSqlDatabase::database(dal->getConnectionName());
            dal->customize(query);
        
            QSqlQuery queryList(query,db);
            while(queryList.next())
            {
                QString key;
                QSqlRecord r=queryList.record();
                struct Tuple t;
                int i=0;
                t.itemType=(SBIDBase::sb_type)queryList.value(i++).toInt();
                t.songID=Common::parseIntFieldDB(&r,queryList.value(i++).toInt());
                t.songTitle=queryList.value(i++).toString();
                t.performerID=Common::parseIntFieldDB(&r,queryList.value(i++).toInt());
                t.performerName=queryList.value(i++).toString();
                t.albumID=Common::parseIntFieldDB(&r,queryList.value(i++).toInt());
                t.albumTitle=queryList.value(i++).toString();
        
                switch(t.itemType)
                {
                case SBIDBase::sb_type_song:
                    t.display=QString("%1 - song by %2").arg(t.songTitle).arg(t.performerName);
                    key=SBIDSong::createKey(t.songID);
                    break;
        
                case SBIDBase::sb_type_album:
                    t.display=QString("%1 - album by %2").arg(t.albumTitle).arg(t.performerName);
                    key=SBIDAlbum::createKey(t.albumID);
                    break;
        
                case SBIDBase::sb_type_performer:
                    t.display=QString("%1 - performer").arg(t.performerName);
                    key=SBIDPerformer::createKey(t.performerID);
                    break;
        
                default:
                    qDebug() << SB_DEBUG_ERROR << "Should not come here";
                }
                _searchItems.append(t);
            }
            QModelIndex s=this->index(0,0);
            QModelIndex e=this->index(3,_searchItems.count());
            emit dataChanged(s,e);
        }
        

        As soon as a database connection has been established, an emit populate is issued to retrieve and populate the data structure. Please let me know if anything else is needed.

        raven-worxR 1 Reply Last reply
        0
        • T TheFlyingMooseMan

          Here it is:

          #include "SearchItemModel.h"
          
          #include "Context.h"
          #include "DataAccessLayer.h"
          
          SearchItemModel::SearchItemModel()
          {
          }
          
          int
          SearchItemModel::rowCount(const QModelIndex &parent) const
          {
              Q_UNUSED(parent);
              return _searchItems.count();
          }
          
          int
          SearchItemModel::columnCount(const QModelIndex &parent) const
          {
              Q_UNUSED(parent);
              return 3;
          }
          
          QVariant
          SearchItemModel::data(const QModelIndex &index, int role) const
          {
              Q_UNUSED(role);
              if(_searchItems.count()==0 || !index.isValid())
              {
                  return QVariant();
              }
          
              if(index.row()<_searchItems.count())
              {
                  Tuple t=_searchItems.at(index.row());
                  switch(index.column())
                  {
                  case 0:
                      return t.display;
                      break;
          
                  case 1:		// 	itemid
                      int itemID;
                      switch(t.itemType)
                      {
                          case SBIDBase::sb_type_song:
                              itemID=t.songID;
                              break;
          
                          case SBIDBase::sb_type_performer:
                              itemID=t.performerID;
                              break;
          
                          case SBIDBase::sb_type_album:
                              itemID=t.albumID;
                              break;
          
                          default:
                              itemID=-1;
                      }
                      return itemID;
                      break;
          
                  case 2:		//	itemtype
                      return t.itemType;
                      break;
                  }
              }
              return QVariant();
          }
          
          
          ///	Slots
          void
          SearchItemModel::populate()
          {
              QString query=QString
              (
                  "SELECT  "
                      "t.type, "
                      "t.song_id, "
                      "t.title, "
                      "t.artist_id, "
                      "t.name, "
                      "t.record_id, "
                      "t.title "
                  "FROM "
                      "table t "
              )
                  .arg(SBIDBase::sb_type_song)
                  .arg(SBIDBase::sb_type_album)
                  .arg(SBIDBase::sb_type_performer)
              ;
          
              DataAccessLayer* dal=Context::instance()->getDataAccessLayer();
              QSqlDatabase db=QSqlDatabase::database(dal->getConnectionName());
              dal->customize(query);
          
              QSqlQuery queryList(query,db);
              while(queryList.next())
              {
                  QString key;
                  QSqlRecord r=queryList.record();
                  struct Tuple t;
                  int i=0;
                  t.itemType=(SBIDBase::sb_type)queryList.value(i++).toInt();
                  t.songID=Common::parseIntFieldDB(&r,queryList.value(i++).toInt());
                  t.songTitle=queryList.value(i++).toString();
                  t.performerID=Common::parseIntFieldDB(&r,queryList.value(i++).toInt());
                  t.performerName=queryList.value(i++).toString();
                  t.albumID=Common::parseIntFieldDB(&r,queryList.value(i++).toInt());
                  t.albumTitle=queryList.value(i++).toString();
          
                  switch(t.itemType)
                  {
                  case SBIDBase::sb_type_song:
                      t.display=QString("%1 - song by %2").arg(t.songTitle).arg(t.performerName);
                      key=SBIDSong::createKey(t.songID);
                      break;
          
                  case SBIDBase::sb_type_album:
                      t.display=QString("%1 - album by %2").arg(t.albumTitle).arg(t.performerName);
                      key=SBIDAlbum::createKey(t.albumID);
                      break;
          
                  case SBIDBase::sb_type_performer:
                      t.display=QString("%1 - performer").arg(t.performerName);
                      key=SBIDPerformer::createKey(t.performerID);
                      break;
          
                  default:
                      qDebug() << SB_DEBUG_ERROR << "Should not come here";
                  }
                  _searchItems.append(t);
              }
              QModelIndex s=this->index(0,0);
              QModelIndex e=this->index(3,_searchItems.count());
              emit dataChanged(s,e);
          }
          

          As soon as a database connection has been established, an emit populate is issued to retrieve and populate the data structure. Please let me know if anything else is needed.

          raven-worxR Offline
          raven-worxR Offline
          raven-worx
          Moderators
          wrote on last edited by raven-worx
          #4

          @TheFlyingMooseMan
          you need to call beginResetModel() and endResetModel() at the beginning/end of your populate() method to inform the view about new data.

          --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
          If you have a question please use the forum so others can benefit from the solution in the future

          1 Reply Last reply
          2
          • T Offline
            T Offline
            TheFlyingMooseMan
            wrote on last edited by
            #5

            Thanks for replying!

            I added the beginResetModel() and endResetModel() -- no go.
            The dropdown box still does not appear -- what does appear is a white bar, probably a fourth the size of the lineEdit widget. If a press a couple of characters and then the cursor down/up keys, I see some choices. But no dropdown box.
            On top of that, quitting the app results in a crash.
            I appreciate any additional hints or replies!

            Thanks,
            Roy

            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