QComboBoxのデータモデルの作成





みなさんこんにちは! QtのQComboBoxのようなウィジェットのデータモデルを作成する方法と作成する方法を、2つの方法で共有したいと思います。 記事の最後に、データベースのコンボボックスを1行のコードで埋めるソリューションが表示されます。



メソッド番号1。 完全に手動のモデル作成



QtのすべてのデータモデルはQAbstractItemModelの子孫でなければなりません。 個人的に、私の練習では、コンボボックスは常にSQLデータベースからの列挙を表示しました。 これらは、性別、国、国籍、およびユーザーが1つの項目を選択する必要のあるその他のリストでした。 そのため、モデルを作成するとき、私は常に2つの並行タスクがありました。







  1. ユーザーが判読できるアイテム名を作成する方法は?
  2. 読み取り可能なアイテムを、データベースに書き込む必要があるキーに接続する方法は?


念のため、だれかが理解できない場合は、違いを説明します。 最初の段落は、人間が読める段落の名前です。 私の例では、国籍を選択するためのコンボボックス。 「ロシア語」、「ベルギー語」、「ノルウェー語」などの単語があります。 プログラムのユーザーが画面に表示するもの。 2番目は、プログラムがデータベースに書き込むものです。 条件付きで「サービス値」。 私の例では、「ロシア語」、「ベルギー語」、「ノルウェー語」のタイプの文字列がデータベースに書き込まれます。 これにより、不必要なトラブルなしに、ユーザーに表示されるアイテムの名前を変更できます。 たとえば、国籍の名前を減らすことにより、コンボボックスの幅を減らすタスクを提供しました。 「ロシア語」ではなく「ロシア語」と表示する必要があります。 この場合、ユーザーに表示されるテキストを落ち着いて変更し、タスクを閉じます。 データベースに直接書き込む場合、コンボボックスに表示されるもの。 「ロシア語」->「Rus」を変更すると、データベースの手順を書くように強制されます。 古い名前を新しい名前に変換するため。 エンドユーザーデータベースで選択された国籍が失われないこと。 要するに、各アイテムの2つの記述された名前(人間が読める形式、公式)。 サポートされているコードを作成することをお勧めします。







計画を実施するため。 まず、自分で決定する必要があるQAbstractItemModelメソッドを調べる必要あります。









つまり 「純粋な仮想」メソッドがここにリストされています。 columnCount()を実装する必要があるのは奇妙に思えます。 なぜなら 列が1つであることは明らかです。 次に、 インデックス()および親()は、単純な線形データ構造(リスト)の背景に対して、何らかの形で冗長に見えます。 これらはQTreeViewの階層ツリー型モデルを構築するためにさらに必要です。 したがって、私たち自身のために余分な作業を発明しないために、 QAbstractListModelからモデルクラスを継承することが決定されました 。これはこの場合にも適しています。 また、リストから最後の2つ(「純粋な仮想」)メソッドのみを実装する必要があります。







したがって、国籍のコンボボックスの選択。 モデルの次の実装が判明します。



// 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 ) );
      
      







コンボボックスアイテムのすべての値は、単にQList <DataPair> m_contentに書き込まれます。 。 そして、コンボボックスがNationalityModel :: data()関数に呼び出されたときに発行されます。 初心者が理解することが重要です。 自分のコードでこの関数を明示的に呼び出すのはプログラマーではありません。 コンボボックスは、必要なときにこの関数を参照します! あなたのタスクは、関数がリクエストに応じてこれらの関連データを返すことです。







NationalityModel :: data()は、2つのパラメーターで呼び出されます。 QAbstractItemModel :: data()単純型の必要に応じて:





NationalityModel :: data()の 1回の呼び出しで、リスト内の1つの特定の行に対して1つのロールのデータが返されます。







Qt :: DisplayRole、Qt :: UserRoleが定義されている列挙型ItemDataRoleを使用すると 、 他にそのようなモデルを実装できる理由が明らかになります。 たとえば、いくつかのアイテムのフォントを変更します(Qt :: FontRole) 。 特別な方法で、メニュー項目のテキストを揃えます。 または、ツールチップテキストを設定します。 上記の列挙型を参照してください。 おそらく、あなたは長い間探していたものをそこに見つけるでしょう。







ソースコード例



職場でこのコードを学ぶことに興味があるかもしれません。 これらの目的のために、 小さなアドレス帳の実装が作成されました







github.comからコードをダウンロードする方法
初期プロジェクトのセットアップ:

  1. プロジェクト「git clone https://github.com/stanislav888/AddressBook.git」をダウンロードします
  2. 現在のcd AddressBookディレクトリを変更する
  3. gitサブモジュールの初期化サブモジュールを初期化する
  4. サブモジュールコードをgitサブモジュール更新プロジェクトにアップロードします
  5. プロジェクトを開いてビルドする
  6. プログラムを実行する
  7. すべてが正常であれば、データベースファイルを選択/作成するためのウィンドウが表示されます。 どのようなプログラムを見ることができます。 テストデータを入力するためのボタン「テストデータを入力」があります


ビルドするには、少なくとも5.0のQtを備えたQtCreatorが必要です。 個人的に、Qt 5.5.0とgcc 5.3.1コンパイラを使用してプロジェクトを構築しました。 プロジェクトは収集され、Qt 4.8.1でも実行されます。 データベースをデバッグするには、Firefox SQLite Managerの拡張機能を使用できます。





メソッド番号2。 SQL DBの列挙からの高速モデル作成



もちろん、転送を整理する最も正しい方法。 これは、個別のテーブルの形式でデータベースに保存することです。 そして、フォームのデザイナーのコンボボックスにロードします。 そして、ある種の普遍的なソリューションを持つことが理想的です。 リストごとに個別のモデルクラスを記述する代わりに。







実装にはQSqlQueryModelが必要です 。 これは同様のモデルです。 彼女はQAbstractItemModelの子孫でもありますが、 QTableViewテーブルにQSqlQuery SQLクエリの結果を表示するために使用されます この場合、タスクはこのクラスを適応させることです。 彼が最初の例のようにデータを提供すること。







驚くでしょうが、コードは小さいことがわかりました。



 // 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 ) );
      
      









この実装では、 QSqlQueryModelがすべての作業を行います。 QSqlQueryModel :: data()のロジックをわずかにオーバーライドする必要があります まず、SQLクエリ「SELECT country.id、countryname || '-' || 国コードFROM国







もちろん、プロジェクトコードでは、これはもう少し複雑です。 ただし、そこでデバッグすると、まさにそのような行が形成されます。 クエリには2つの列が表示されます。 主キー(「id」)。 そして、スクリーンショットに表示される人間が読み取れる値。 SQLクエリのすべての結果はQSqlQueryModelのQt :: DisplayRoleにあるためです。 QSqlQueryModelを変更せずに、コンボボックスのモデルとして、単に「id」のリストを表示します。 また、人間が読み取れる値は表示されません。 なぜなら コンボボックスは、モデル(クエリ)の2番目の列を使用しません。 BaseComboModel :: data()の宣言と実装をコメントアウトすると、これが表示されます



スクリーンショットのように国のリストを表示するには、 BaseComboModel :: data()





これにより、 BaseComboModelを使用してQComboBoxのモデルをすばやく簡単に作成できます たとえば、1年の月(「月」)のSQLテーブルがあるとします。 「id」と「monthname」の2つの列はどこにありますか。 次のように、月選択コンボボックスに入力できます。



ui-> monthsCombo-> setModel(新しいBaseComboModel( "monthname"、 "months"、this));

選択した月のid値を取得しますui-> monthsCombo-> itemData(ui-> monthsCombo-> currentIndex()、Qt :: UserRole); 。 ユーザーに見える値を取得ui-> monthsCombo-> currentText(); 。 このコードは、他のすべての場合よりもはるかにコンパクトです。 この状況のほとんどの開発者は、データベースに個別のクエリ(QSqlQuery)を作成します。 次に、ループで、 QComboBox :: addItem()使用して、受信したエントリをコンボボックスに追加します 。 これはもちろん機能しますが、最も美しいソリューションではありません。







練習する



ここですべてがどのように機能し、どのように機能するかを誰もが理解しているとは思わない これには、モデルの実装に非常に具体的な経験が必要だからです。 この場合、記事の時間は無駄になりません。 与えられたコードを実際に使用してみましょう。 その後、何が最終的にそれを理解します。







これを行うための2つのオプション:



  1. 私のアプリケーションに基づいた実験-上記のアドレス帳。 BaseComboModelのヘッダーと実装は、プロジェクトに既に存在します。 以下の例はそれに基づいています。



  2. SQLデータベースで動作する他のアプリケーションを使用します。 SQLiteである必要はありません。 任意のベースが行います! 上記のコードを任意のフォームの実装ファイルに貼り付けるだけです。

    もちろん、 BaseComboModelのファイル、ヘッダー、実装を個別に作成するのが正しいでしょう。 今のところ、これを行うのが面倒だと思います。 もちろん、コンパイルエラーに少し対処する必要があります。 しかし、彼らは簡単になります。 コンボボックスのデータを取得するテーブル。 「id」列が存在する必要があります


BaseComboModelコンストラクターパラメーター(const QString&columns、const QString&queryTail、QObject * parent = 0)





次に、実験するフォームにQComboBoxを追加する必要があります。 私の場合、それはaddressbookmainwindow.uiになります。 新しいウィジェット名ui-> comboBox











次に、このコンボボックスにさまざまな方法で入力します
ui-> comboBox-> setModel(新しいBaseComboModel( "countryname"、 "country"、this));

「country.id、countryname FROM countryを選択してください

国のリストだけ
ui-> comboBox-> setModel(新しいBaseComboModel( "countryname"、 "country WHERE countrycode IN( 'US'、 'RU'、 'C​​N')"、this));

"country.idを選択、国名FROM国WHERE国コードIN(" US "、" RU "、" CN ")"

コードによって選択されたいくつかの国。

ui-> comboBox-> setModel(新しいBaseComboModel( "lastname"、 "persons"、this));

「persons.idを選択して、姓からFROM名」

データベースに記録されている姓のリスト。 何であれ、「テストデータを記入」ボタンをクリックします
ui-> comboBox-> setModel(新しいBaseComboModel( "lastname || '-' || email"、 "左のJOINアドレスをON a.id = person.addressid"にすると);

「persons.id、姓を選択|| '-' || 人からメールを送るa.id = persons.addressidとしてのアドレスとして参加します "

メールアドレス付きの姓のリスト。 「||」を忘れないでください SQLiteのみの文字列連結演算子。 他のベースについては、連結をやり直す必要があります
ui-> comboBox-> setModel(新しいBaseComboModel( "lastname || '-' || countryname"、 "persons INNER JOINアドレスAS a ON a.id = people.addressid INNER JOIN country AS c ON a.countryid = c。 id "、this));;

「persons.id、姓を選択|| '-' || a.id = people.addressidとしての内部結合アドレスからの国名a.countryid = c.idにおけるcとしての内部結合国。

関連する国の姓のリスト


もちろん、 「JOIN」「WHERE」を使用したこれらのトリックはすべて面白そうです。 しかし、ほとんどの場合、それらは必要ありません。 したがって、コンストラクターで2つのパラメーターを使用することが決定されました。 そこにSQLクエリ全体を送信する代わりに。 すべてのリストを1つのテーブルに保存する場合。 そして、これらの転送を追加のキーで分離します。 このキーの値を使用して、3番目のパラメーターを作成することをお勧めします。 毎回WHEREを使用する代わりに。



選択したレコードの「ID」を取得する方法を繰り返します

ui-> comboBox-> itemData(ui-> comboBox-> currentIndex()、Qt :: UserRole);



おわりに







うまくいけば、コードの複雑さにもかかわらず。 あなたは自分に役立つ何かを学びました。 例として、ここでAddressBookアプリケーションについて詳しく知りたい場合。 記事「SQLデータベースを使用したQtフォームからのデータ交換の自動化」を参照してください








All Articles