別のモデル ビューのデリゲートにモデル ビュー (ListView や GridView など) を含めることはできますか?
デリゲートにも ListView (LV2) がある ListView (LV1) を使用しています。LV2 の前後のすべてが正しく表示されます (これは LV1 のデリゲートにあります)。ただし、LV1には何も表示されません。
私のモデルは次のものです: http://qt-project.org/wiki/How_to_use_a_QSqlQueryModel_in_QML。Qtの外部で実行すると正しい情報が返されるため、SQLクエリは正しいと思います。デバッグ メッセージによると、すべてのクエリが明確に実行されています。
2 つのクエリがあり、一方が他方で更新されています。それはうまくいきます。
図 1 はデバッグ メッセージを示しており、QML がデータ関数を呼び出し、実際にデータベースから情報が返されることを示しています。
図 2 は、図 1 の時点での QML ウィンドウを示しています。レイアウトが乱れて何も表示されていないことがはっきりとわかります。
Verbatim #1 は QML コードです。
Verbatim #2 はメインの抜粋であり、updateBindings というシグナルを介してメインモデルをセカンダリ モデルに接続していることを示しています。
Verbatim #3 は私の更新された SqlQueryModel です。チュートリアルとの主な違いは、シグナルとスロットです。
Verbatim #4 は参照用です。このクラスは Verbatim #3 で使用されているためです。
どんなインプットも大切です。私はここ数日間、これに苦労してきました。ありがとう。
逐語#1(QML):
import QtQuick 1.1
import "Styles"
Rectangle {
id : window;
width : 750
height : 500
signal searchSignal(string msg)
signal resetSignal()
property variant subtypes: {"S":"Status (digital)", "V":"Value (analog)"}
property variant phases: {'66': 'Final Mass Properties', '67': 'Final Thruster Aignment/Solar Array Inst.', '60': 'Final Functional Performance', '114': 'Maneuver (Wheel Mode) Overlay (LTO)', '88': 'Troubleshooting Test Phase 9', '89': 'Troubleshooting Test Phase 10', '111': 'Battery 1 Reconditioning Overlay (LTO)', '110': 'Battery Recharge Overlay (LTO)', '113': 'Maneuver (SK Mode) Overlay (LTO)', '112': 'Battery 2 Reconditioning Overlay (LTO)', '68': 'Flight Battery Functional', '83': 'Troubleshooting Test Phase 4', '80': 'Troubleshooting Test Phase 1', '81': 'Troubleshooting Test Phase 2', '86': 'Troubleshooting Test Phase 7', '87': 'Troubleshooting Test Phase 8', '84': 'Troubleshooting Test Phase 5', '85': 'Troubleshooting Test Phase 6', '24': 'Post T/V Performance ( Ambient)', '26': 'Alignments - Pre Dynamics', '27': 'Antenna Installation', '20': 'Cold (Equinox) Functional Plateau', '21': 'Transition Monitor (cold to hot)', '22': 'Hot ( Winter Solstice)', '82': 'Troubleshooting Test Phase 3', '28': 'Solar Array Installation', '40': 'CATR', '41': 'ESD (PFM Only)', '1': 'North Panel Electrical integration', '3': 'SSM/Bus Module Electrical Integration', '2': 'South Panel Electrical integration', '5': 'South and bus Electrical Integration', '4': 'North and Bus Electrical integration', '6': 'S/C Electrical Integration', '75': 'Hazardous Processing Facility Operations', '39': 'Final Alignment Verif. CATR Preps', '77': 'Launch Complex Performance', '76': 'HPF Payload testing', '108': 'Lunar Eclipse Event Overlay (LTO)', '109': 'Battery Discharge Overlay (LTO)', '70': 'Launch Base Functional Performance', '102': 'On Orbit', '103': 'Station Keeping', '100': 'Prime Shift Orbit Raising', '101': 'Off Shift Orbit Raising', '106': 'Quiescent Non-Eclipse State of Health (LTO)', '107': 'Earth Eclipse Season Overlay (LTO)', '104': 'Eclipse', '105': 'Post Eclipse', '10': 'Panel RF Testing', '13': 'Integrated System Reference Performance', '12': 'End to End Satting', '15': 'Transition Monitoring (Ambient to Hot Solstice)', '14': 'Pre Thermal Vacuum performance (Ambient)', '16': 'Summer Solstice Functional Plateau', '18': 'Transition Monitoring (Hot to Cold)', '31': 'Sine Vibration', '30': 'Acoustic Vibration', '36': 'Post Dynamics Performance', '35': 'Deployments', '34': 'Launch Vehicle Adapter Fit Check', '65': 'Propulsion Global Helium'}
ListView {
anchors.fill: parent
focus: true
highlightRangeMode: ListView.StrictlyEnforceRange
orientation: ListView.Horizontal
snapMode: ListView.SnapOneItem
model: tcModel
delegate: Component {
Item {
id: item
width: window.width; height: window.height
Flickable {
id: mainScrollView
contentHeight: parent.height
contentWidth: parent.width
anchors.fill: parent
clip: true
focus: true
interactive: true
Column{
id: dataCol
spacing: 10
/* Buttons */
Row{
width: window.width
Button{
text: "Go"
action.onClicked: searchSignal("TLM_NO LIKE '" + num.text + "%'")
bgColor: "lightgreen"
width: window.width/10; height: window.width/30
}
Button{
text: "Reset"
action.onClicked: resetSignal()
bgColor: "lightgrey"
width: window.width/10; height: window.width/30
}
}
/* */
Grid{
columns: 5
spacing: 10
Text {
text: "Mnemonic"
font.bold: true
}
Text {
text: "Name"
font.bold: true
}
Text {
text: "Type"
font.bold: true
}
Text {
text: "Subtype"
font.bold: true
}
Text {
text: "Category"
font.bold: true
}
/* NEW LINE */
TextEdit {
id: num
text: TLM_NO
}
Text {
text: TLM_NAME
}
Text {
text: TLM_TYPE
}
Text {
text: (SUBTYPE ? subtypes[SUBTYPE] : "Unknown")
}
Text {
text: TLM_CATEGO
}
} /* End grid */
Separator{}
Text{
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: "<u>Limits</u>"
id: limitLabel
}
/* Limits */
ListView {
//anchors.top: limitLabel.bottom
header:
Row{
height: 30
clip: true
anchors.margins: 4
Text {
text: "Phase name"
font.bold: true
}
Text {
text: "Red low"
font.bold: true
}
Text {
text: "Yellow low"
font.bold: true
}
Text {
text: "Yellow high"
font.bold: true
}
Text {
text: "Red high"
font.bold: true
}
}
delegate: Item {
id: delegate
width: delegate.ListView.view.width;
height: 30
clip: true
anchors.margins: 4
Row {
anchors.margins: 4
anchors.fill: parent
spacing: 4;
Text {
text: PHASE_NO?phases[PHASE_NO]:"N/A"
}
Text {
text: CRIT_LO?CRIT_LO:"N/A"
}
Text {
text: NOM_LO?NOM_LO:"N/A"
}
Text {
text: NOM_HI?NOM_HI:"N/A"
}
Text {
text: CRIT_HI?CRIT_HI:"N/A"
}
}
}
model: limitModel
height: window.height / 3
} /* End limits grid view*/
Separator{}
Text{
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: "end limits"
}
}/* End column*/
}
}
}
}
}
Verbatim #2 (main.cpp 、抜粋):
/* Let's create all the models associated with this given user form.*/
/* We need the viewer to connect all the signals and to set context properties.*/
QmlApplicationViewer viewer;
SqlQueryModel *mainModel;
QList<SqlQueryModel*>::iterator umIt;
QHash<QString, SqlQueryModel*> modelCnx = QHash<QString, SqlQueryModel*>();
for(umIt = selectedUF.userModels.begin(); umIt != selectedUF.userModels.end(); umIt++){
SqlQueryModel *model = *umIt;
/* Let's go through each binding of the UserModel and connect create the bindings in the model. */
model->exec();
viewer.rootContext()->setContextProperty(model->modelName, model);
/* If this is the selected search model, let's save it to connect the signals later. */
if (model->modelName == selectedUF.searchModel){
mainModel = model;
}else{
QObject::connect(mainModel, SIGNAL(bindedValueChanged(QString, QString, QVariant)),
model, SLOT(updateBindings(QString,QString,QVariant)));
}
}
viewer.setMainQmlFile(QString("qml/" + selectedUF.qml));
QObject::connect((QObject*)viewer.rootObject(), SIGNAL(searchSignal(QString)),
mainModel, SLOT(search(QString)));
QObject::connect((QObject*)viewer.rootObject(), SIGNAL(resetSignal()),
mainModel, SLOT(reset()));
viewer.showExpanded();
return app->exec();
Verbatim #3 (sqlquerymodel.cpp):
#include "sqlquerymodel.h"
SqlQueryModel::SqlQueryModel(QObject *parent) :
QSqlQueryModel(parent)
{
this->modelName = "";
this->query = "";
bindings = QHash<QString, QList<ModelBinding> >();
}
void SqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db)
{
if(query.length() == 0){
this->query = query;
}
QSqlQueryModel::setQuery(query, db);
generateRoleNames();
QSqlError le = this->lastError();
if (le.isValid()){
QString errMsg("Query was:\n%1\n\nError:\n%2");
QMessageBox::critical(0, "Query error", errMsg.arg(query).arg(le.text()));
return;
}
}
void SqlQueryModel::setQuery(const QSqlQuery &query)
{
QSqlQueryModel::setQuery(query);
generateRoleNames();
QSqlError le = this->lastError();
if (le.isValid()){
QString errMsg("Query was:\n%1\n\nError:\n%2");
qDebug() << errMsg.arg(query.lastQuery()).arg(le.text());
/* We're not using a MessageBox because it causes a segfault for some reason. */
//QMessageBox::critical(0, "Query error", errMsg.arg(query.lastQuery()).arg(le.text()));
return;
}
}
/**
* @brief SqlQueryModel::exec This function prepares and executes the query.
*/
void SqlQueryModel::exec()
{
qDebug() << "Executing query on model" << this->modelName;
/* Let's create a QSqlQuery. It will store the query and we'll bind values to it.*/
QSqlQuery sQuery;
/* If we initialize the query with the string, then we CANNOT use bind (won't work and won't show any error).*/
sQuery.prepare(this->query);
/** Now, let's go through all the models associated to this instance.
* For each of them, we'll bind its value. Note that we're avoiding making a copy by using the adresses
* cf. : http://stackoverflow.com/questions/17106243/qt-iterator-not-accessing-the-correct-object
**/
QHash<QString, QList<ModelBinding> >::iterator bindingsIt;
for (bindingsIt = bindings.begin(); bindingsIt != bindings.end(); bindingsIt++){
QList<ModelBinding>::iterator eachBindingIt;
QList<ModelBinding>& curBinding = *bindingsIt;
for(eachBindingIt = curBinding.begin(); eachBindingIt != curBinding.end(); eachBindingIt++){
ModelBinding& binding = *eachBindingIt;
binding.bindToQuery(&sQuery);
}
}
/* Let's not forget to execute this query, or nothing will be displayed in the QML. */
sQuery.exec();
qDebug() << "----------------";
qDebug() << sQuery.lastQuery();
QMapIterator<QString, QVariant> i(sQuery.boundValues());
while (i.hasNext()) {
i.next();
qDebug() << i.key().toAscii().data() << "="
<< i.value().toString().toAscii().data();
}
qDebug() << "----------------";
this->setQuery(sQuery);
}
void SqlQueryModel::generateRoleNames()
{
QHash<int, QByteArray> roleNames;
for( int i = 0; i < record().count(); i++) {
roleNames[Qt::UserRole + i + 1] = record().fieldName(i).toAscii();
}
qDebug() << "Generating role names for" << modelName;
setRoleNames(roleNames);
}
void SqlQueryModel::search(QString str){
QString nQuery (query);
nQuery.append(" WHERE ").append(str);
qDebug() << "Set query to: " << nQuery;
this->setQuery(nQuery);
QSqlError le = this->lastError();
if (le.isValid()){
QString errMsg("An error occurred while loading the file.\n\nQuery was:\n%1\n\nError:\n%2");
QMessageBox::critical(0, "Database error", errMsg.arg(nQuery).arg(le.text()));
}
}
QVariant SqlQueryModel::data(const QModelIndex &index, int role) const
{
QVariant value = QSqlQueryModel::data(index, role);
if(role < Qt::UserRole){
value = QSqlQueryModel::data(index, role);
}else{
int columnIdx = role - Qt::UserRole - 1;
QModelIndex modelIndex = this->index(index.row(), columnIdx);
value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
qDebug() << modelName << ":" << record().fieldName(columnIdx) << "=" << value;
emit bindedValueChanged(modelName, record().fieldName(columnIdx), value);
}
return value;
}
void SqlQueryModel::reset()
{
qDebug() << "Resetting original SQL query to: " << query;
this->setQuery(query);
}
void SqlQueryModel::updateBindings(QString modelName, QString col, QVariant val)
{
/** Now, let's go through all the models associated to this instance.
* We're going to see if the new signal we got is used for this model (for model name and column name).
* If so, we'll assigned it and then we'll execute this query by calling exec().
**/
bool anyValueChanged = false;
QHash<QString, QList<ModelBinding> >::iterator bindingsIt;
for (bindingsIt = bindings.begin(); bindingsIt != bindings.end(); bindingsIt++){
QList<ModelBinding>::iterator eachBindingIt;
QList<ModelBinding>& curBinding = *bindingsIt;
for(eachBindingIt = curBinding.begin(); eachBindingIt != curBinding.end(); eachBindingIt++){
ModelBinding& binding = *eachBindingIt;
if(bindingsIt.key() == modelName && binding.column == col){
binding.value = val;
anyValueChanged = true;
}
}
}
if (anyValueChanged){
this->exec();
}
}
Verbatim #4 (modelbinding.cpp):
#include "modelbinding.h"
ModelBinding::ModelBinding(QString placeholder, QString column, QVariant value)
{
this->placeholder = placeholder;
this->column = column;
this->value = value;
}
void ModelBinding::bindToQuery(QSqlQuery *sQuery)
{
sQuery->bindValue(placeholder, value);
}