All data models in Qt must be derived from QAbstractItemModel . Personally, in my practice, combo boxes always displayed an enumeration from the SQL database. These were gender, country, nationality and some other lists from which the user had to select one item. Therefore, when creating a model, I always had two parallel tasks:
To implement the plan. First of all, you need to look at which of the QAbstractItemModel methods you are required to determine:
')
// nationalitymodel.h // #pragma once #include <QAbstractListModel> class NationalityModel : public QAbstractListModel { Q_OBJECT typedef QPair<QVariant, QVariant> DataPair; QList< DataPair > m_content; public: explicit NationalityModel( QObject *parent = 0 ); virtual QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const; virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const; }; // nationalitymodel.cpp #include "nationalitymodel.h" NationalityModel::NationalityModel(QObject *parent) : QAbstractListModel(parent) { m_content << qMakePair( DataPair::first_type(), DataPair::second_type( "" ) ) << qMakePair( DataPair::first_type( "Russian" ), DataPair::second_type( "russian" ) ) << qMakePair( DataPair::first_type( "Belgian" ), DataPair::second_type( "belgian" ) ) << qMakePair( DataPair::first_type( "Norwegian" ), DataPair::second_type( "norwegian" ) ) << qMakePair( DataPair::first_type( "American" ), DataPair::second_type( "american" ) ) << qMakePair( DataPair::first_type( "German" ), DataPair::second_type( "german" ) ); } QVariant NationalityModel::data( const QModelIndex &index, int role ) const { const DataPair& data = m_content.at( index.row() ); QVariant value; switch ( role ) { case Qt::DisplayRole: { value = data.first; } break; case Qt::UserRole: { value = data.second; } break; default: break; } return value; } int NationalityModel::rowCount(const QModelIndex &/*parent*/) const { return m_content.count(); } // addressbookmainwindow.cpp. , ( AddressBookMainWindow::AddressBookMainWindow() ) ui->nationalityCombo->setModel( new NationalityModel( this ) );
All combo box item values ​​are simply recorded in a QList <DataPair> m_content; . And then they are issued when the combobox calls on the function NationalityModel :: data () . Beginners are important to understand. No programmer explicitly calls this function in his code. And combobox refers to this function when he needs! Your task is that the function would give this actual data on request.
In one call, NationalityModel :: data () returns data of one role for one, specific row in the list.
If you refer to the enum ItemDataRole , where Qt :: DisplayRole, Qt :: UserRole are defined . It becomes clear why you can still implement such a model. For example, change the font of some items (Qt :: FontRole) . Align the text of the menu item, as something special. Or set the text of the tooltip. See the mentioned enum. Perhaps you will find there what you have been looking for a long time.
You may be interested to study this code in the work. For these purposes, a small address book implementation was created .
Of course, the most correct way to organize transfers. This is to store them in the database, as separate tables. And load in the form constructors. And it would be ideal to have some kind of universal solution. Instead of writing separate classes of models for each listing.
For implementation, we need QSqlQueryModel . This is a similar model. It is also a QAbstractItemModel heir, but is used to display the results of the QSqlQuery SQL query in the QTableView table . In this case, our task is to adapt this class. What would he give the data as in the first example.
// addressdialog.h /// #pragma once #include <QSqlQueryModel> class BaseComboModel : public QSqlQueryModel { Q_OBJECT QVariant dataFromParent(QModelIndex index, int column) const; public: explicit BaseComboModel( const QString &columns, const QString &queryTail, QObject *parent = 0 ); virtual QVariant data(const QModelIndex &item, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex &parent) const; }; // basecombomodel.cpp #include "basecombomodel.h" #include <QSqlQuery> namespace { enum Columns // Depends with 'query.prepare( QString( "SELECT ... ' { Id, Data, }; } BaseComboModel::BaseComboModel( const QString& visualColumn, const QString& queryTail, QObject *parent ) : QSqlQueryModel( parent ) { QSqlQuery query; query.prepare( QString( "SELECT %1.id, %2 FROM %3" ).arg( queryTail.split( ' ' ).first() ).arg( visualColumn ).arg( queryTail ) ); // Ie query.prepare( "SELECT country.id, countryname || ' - ' || countrycode FROM country" ); query.exec(); QSqlQueryModel::setQuery( query ); } QVariant BaseComboModel::dataFromParent( QModelIndex index, int column ) const { return QSqlQueryModel::data( QSqlQueryModel::index( index.row() - 1 // "- 1" because make first row empty , column ) ); } int BaseComboModel::rowCount(const QModelIndex &parent) const { return QSqlQueryModel::rowCount( parent ) + 1; // Add info about first empty row } QVariant BaseComboModel::data(const QModelIndex & item, int role /* = Qt::DisplayRole */) const { QVariant result; if( item.row() == 0 ) // Make first row empty { switch( role ) { case Qt::UserRole: result = 0; break; case Qt::DisplayRole: result = "(please select)"; break; default: break; } } else { switch( role ) { case Qt::UserRole: result = dataFromParent( item, Id ); break; case Qt::DisplayRole: result = dataFromParent( item, Data ); break; default: break; } } return result; } // (addressdialog.ui) ui->countryCombo->setModel( new BaseComboModel( "countryname || ' - ' || countrycode", "country", this ) );
In this implementation, all the work is done by QSqlQueryModel . You just need to override the logic of QSqlQueryModel :: data () a little . To begin with, imagine that a SQL query is written to the model “SELECT country.id, countryname || '-' || countrycode FROM country " .
Thus, you can quickly and easily make models for QComboBox using BaseComboModel. For example, you have a SQL table of months per year (“months”). Where two columns, "id" and "monthname". You can complete the month selection box as follows:
ui-> monthsCombo-> setModel (new BaseComboModel ("monthname", "months", this));
Get the value of the “id” of the selected month ui-> monthsCombo-> itemData (ui-> monthsCombo-> currentIndex (), Qt :: UserRole); . Get the value visible to ui-> monthsCombo-> currentText (); . This code is much more compact than all other cases. Most developers in this situation write separately to the database (QSqlQuery) . And then, in a loop, add the resulting entries to the combobox via QComboBox :: addItem () . This is certainly a working, but not the most beautiful solution.
I suspect, not everyone understood how everything is arranged and works here. Just because it requires a very specific experience in the implementation of their models. In this case, what would be your time on the article was not in vain. Let's just try to use the code in practice. What then, over time, to understand it.
Next, you need to add to the form QComboBox with which you will experiment. In my case it will be addressbookmainwindow.ui . The name of the new widget ui-> comboBox
![]() | ui-> comboBox-> setModel (new BaseComboModel ("countryname", "country", this)); "SELECT country.id, countryname FROM country" . Just a list of countries |
![]() | ui-> comboBox-> setModel (new BaseComboModel ("countryname", "country WHERE countrycode IN ('US', 'RU', 'CN')", this)); "SELECT country.id, countryname FROM country WHERE countrycode IN (" US "," RU "," CN ")" . Some countries selected by code. |
![]() | ui-> comboBox-> setModel (new BaseComboModel ("lastname", "persons", this)); "SELECT persons.id ,, lastname FROM persons" . List of names recorded in the database. What would they be, you must click the button "Fill test data" |
![]() | ui-> comboBox-> setModel (new BaseComboModel ("lastname || '-' || email", "persons LEFT JOIN address AS a ON a.id = persons.addressid", this)); "SELECT persons.id, lastname || '-' || email FROM persons LEFT JOIN address AS a ON a.id = persons.addressid ” . List of names with email addresses. Do not forget that "||" string concatenation operator in SQLite only. For other bases, you will need to redo concatenation. |
![]() | ui-> comboBox-> setModel (new BaseComboModel ("lastname || '-' || countryname", "persons INNER JOIN address AS a ON a.id = persons.addressid INNER JOIN country AS with ON a.countryid = c. id ", this)); "SELECT persons.id, lastname || '-' || countryname FROM persons INNER JOIN address AS a ON a.id = persons.addressid INNER JOIN country AS with ON a.countryid = c.id " . List of names with relevant countries |
I hope, despite the complexity of the code. You have learned something useful for yourself. If you want to know more about the AddressBook application given here for the sake of example. See the article "Automating the exchange of Qt form data with a SQL database."
Source: https://habr.com/ru/post/329228/
All Articles