Qt / QML:是否允许将模型视图作为委托?

时间:2013-06-24 10:33:11

标签: qt listview model-view-controller qml

是否可以在另一个模型视图的委托中拥有模型视图(例如ListView或GridView)?

我正在使用ListView(LV1),其委托也有一个ListView(LV2)。 LV2之前和之后的所有内容都正确显示(并且在LV1的委托中)。但是,LV1中没有显示任何内容。

我的模型来自:http://qt-project.org/wiki/How_to_use_a_QSqlQueryModel_in_QML。我认为SQL查询是正确的,因为它在外部运行到Qt时返回正确的信息。根据调试消息,所有查询都清楚地执行。

我有两个查询,一个用另一个查询。这很好。

图#1显示了调试消息,并指出QML确实调用了数据函数,并确实从数据库返回了信息。

图#2显示了图#1时的QML窗口:我们可以清楚地看到布局混乱并且没有显示任何内容。

Verbatim#1是QML代码。

Verbatim#2是主要内容的摘录,显示我通过名为updateBindings的信号将模型连接到辅助模型。

Verbatim#3是我更新的SqlQueryModel。与教程的主要区别在于信号和插槽。

Verbatim#4仅供参考,因为此类用于逐字#3。

任何输入都很有价值,我几天来一直在努力解决这个问题。感谢。

Verbatim#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);
}

Figure #1 Figure #2

1 个答案:

答案 0 :(得分:1)

基础ListView委托中的项目的大小适合窗口的高度和宽度。我相信这会有效地隐藏任何其他代表从根级别ListView,因为第一个代表将占用整个屏幕。