c++ - QML में उपयोग के लिए सामान्य ऑब्जेक्ट मॉडल कैसे बनाऊँ?




qt generics model qtquick2 (2)

मैं जानना चाहूंगा कि कोई भी मैक्रो या QObject की संपत्ति के रूप में क्यूटी मॉडल को कैसे पंजीकृत किया जाए।

उदाहरण के लिए, मेरे पास AnimalModel ( http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitmodel ) है।

मुझे पता है कि मैं इसे QuickView के मूल संदर्भ में पास कर सकता हूं

QuickView view;
view.rootContext()->setContextProperty("myModel", &model);

यदि मुझे QMbject के माध्यम से QMbject पंजीकृत है, तो मैं भी इस ऑब्जेक्ट को देखने के लिए पास कर सकता हूँ:

view.rootContext()->setContextProperty("obj", pDataObject);

लेकिन क्या अगर मैं किसी भी डेटा के मॉडल वाले QObject चाहते हैं?

उदाहरण के लिए:

class DataObject : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
    ...

    AnimalModel m_modelAnimals;

    //Is this possible in any way?
    //Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
};

हर उदाहरण जो मैंने अब तक पाया है कि QAbstractListModel को रूट करने के लिए QAbstractListModel को कैसे पारित किया QAbstractListModel । लेकिन कोई भी इसे QObject प्रॉपर्टी के रूप में कैसे उपयोग नहीं करें

(मुझे पता है कि QQmlListProperty लेकिन QQmlListProperty आंशिक रीफ्रेश का समर्थन नहीं करता है.सभी Qml ऑब्जेक्ट्स को पुनर्निर्माण के लिए हमेशा आवश्यक होता है)


Answers

//Is this possible in any way?
//Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)

हां यह है, क्या आपने कोशिश नहीं की? बेशक, यह एक AnimalModel नहीं होगा बल्कि एक AnimalModel * , लेकिन जब तक मॉडल QAbstractListModel संभालते हैं, तो यह आपको बस की आवश्यकता है। आपको NOTIFY भाग की भी जरूरत नहीं है, परिवर्तन के रूप में, मॉडल के अंतर्गत आंतरिक रूप से किसी भी रूप में दिखाई देगा। modelAnimalsChanged केवल जब आप एक अलग मॉडल के साथ पूरे मॉडल को बदलते हैं, और स्वाभाविक रूप से, सूचित संकेत के बिना एक संपत्ति का उपयोग करने के बारे में क्यूएमएल की चेतावनियों को बंद करने के लिए बदल जाता है। जब मॉडल ऑब्जेक्ट में कोई परिवर्तन नहीं होता है तो बाद में ऐसा करने का एक क्लीनर तरीका है कि सिर्फ स्लॉट से एक AnimalModel * या एक Q_INVOKABLE

यदि आप वास्तव में एक लचीला मॉडल चाहते हैं, तो आप ऐसा कर सकते हैं जो QObject * स्टोर QObject * , फिर QML से आप मनमाना गुणों के साथ मनमाना वस्तुएं बना सकते हैं, और मॉडल में जोड़ सकते हैं। फिर मॉडल से आपके पास एक एकल object भूमिका होती है जो ऑब्जेक्ट देता है, और आप उस वस्तु को पुनर्प्राप्त करने के लिए ऑब्जेक्ट की क्वेरी और उपयोग कर सकते हैं। जबकि एक "शास्त्रीय" सूची मॉडल का कार्यान्वयन एक मॉडल को स्थिर, निर्धारित स्कीमा के साथ परिभाषित करेगा, इस दृष्टिकोण का उपयोग करके मॉडल में "अनाकार" वस्तुओं को अलग-अलग गुणों के साथ अनुमति देता है

स्वाभाविक रूप से, इस प्रकार कुछ प्रकार की सुरक्षा की आवश्यकता होती है, उदाहरण के लिए इस तरह के एक मॉडल में प्रत्येक ऑब्जेक्ट के लिए एक property int type , और इसके आधार पर आप ऑब्जेक्ट के लिए उपलब्ध गुणों को निर्धारित कर सकते हैं। मेरा सामान्य दृष्टिकोण एक प्रतिनिधि के लिए एक Loader , और यह ऑब्जेक्ट को अलग-अलग QML UI कार्यान्वयन के लिए डेटा स्रोत के रूप में पास करता है, जो उस ऑब्जेक्ट प्रकार को दृश्यमान करता है जिसे वह इन्स्ताइज़ करता है। इस तरह आपके पास मॉडल में अलग-अलग ऑब्जेक्ट हैं, और विभिन्न QML आइटम देखने के प्रतिनिधियों के रूप में।

अंतिम "सभी ट्रेडों के जैक" सूची / मॉडल ऑब्जेक्ट बनाने के लिए अंतिम चरण QQmlListProperty और Q_CLASSINFO("DefaultProperty", "container") को इसके लिए लागू करना है, जिससे आप दोनों को सूची / मॉडल गतिशील रूप से लिख सकते हैं, या QML के घोषणात्मक का उपयोग कर सकते हैं। वाक्य - विन्यास। यह भी ध्यान रखें कि इस समाधान के साथ, आप ऐसे मॉडल से जोड़ सकते हैं या निकाल सकते हैं, यहां तक ​​कि घोषणात्मक रूप से तत्काल वस्तुओं को हटा सकते हैं।

इसके अलावा, आपके उपयोग परिदृश्य के आधार पर, आपको मॉडल के लिए या तो qmlRegisterType() या qmlRegisterUncreatableType() हो सकता है

ठीक है, दूसरी तरफ, यह "किसी भी डेटा का मॉडल" जैसा दिखता है, जिसका अर्थ आपको स्कीमा-कम मॉडल नहीं बल्कि अलग-अलग स्कीमा मॉडल का मतलब था। उस मामले में, एक AnimalModel * लौटने के बजाय, आप QAbstractListModel * या यहां तक ​​कि एक QObject * उपयोग कर सकते हैं - यह QML में भी काम करेगा, क्योंकि यह मेटा सिस्टम के माध्यम से गतिशीलता को नियोजित करता है। लेकिन किसी भी दर पर, स्कीमा-कम मॉडल अधिक शक्तिशाली और लचीले होते हैं, और उन्हें परिभाषित करने के लिए C ++ कोड की आवश्यकता नहीं होती है, यह सभी अकेले क्यूएम से काम कर सकता है

class List : public QAbstractListModel {
    Q_OBJECT
    QList<QObject *> _data;

    Q_PROPERTY(int size READ size NOTIFY sizeChanged)
    Q_PROPERTY(QQmlListProperty<QObject> content READ content)
    Q_PROPERTY(QObject * parent READ parent WRITE setParent)
    Q_CLASSINFO("DefaultProperty", "content")
public:
    List(QObject *parent = 0) : QAbstractListModel(parent) { }
    int rowCount(const QModelIndex &p) const { Q_UNUSED(p) return _data.size(); }
    QVariant data(const QModelIndex &index, int role) const {
        Q_UNUSED(role)
        return QVariant::fromValue(_data[index.row()]);
    }
    QHash<int, QByteArray> roleNames() const {
        static QHash<int, QByteArray> * pHash;
        if (!pHash) {
            pHash = new QHash<int, QByteArray>;
            (*pHash)[Qt::UserRole + 1] = "object";
        }
        return *pHash;
    }
    int size() const { return _data.size(); }
    QQmlListProperty<QObject> content() { return QQmlListProperty<QObject>(this, _data); }

public slots:
    void add(QObject * o) {
        int i = _data.size();
        beginInsertRows(QModelIndex(), i, i);
        _data.append(o);
        o->setParent(this);
        sizeChanged();
        endInsertRows();
    }

    void insert(QObject * o, int i) {
        beginInsertRows(QModelIndex(), i, i);
        _data.insert(i, o);
        o->setParent(this);
        sizeChanged();
        endInsertRows();
    }

    QObject * take(int i) {
        if ((i > -1) && (i < _data.size())) {
            beginRemoveRows(QModelIndex(), i, i);
            QObject * o = _data.takeAt(i);
            o->setParent(0);
            sizeChanged();
            endRemoveRows();
            return o;
        } else qDebug() << "ERROR: take() failed - object out of bounds!";
        return 0;
    }

    QObject * get(int i) {
        if ((i > -1) && (i < _data.size())) return _data[i];
        else  qDebug() << "ERROR: get() failed - object out of bounds!";
        return 0;
    }
signals:
    void sizeChanged();
};

उसके बाद, आपके बाद qmlRegisterType<List>("Core", 1, 0, "List"); आप इसका इस्तेमाल किसी भी तरह से कर सकते हैं - यह किसी भी QObject या व्युत्पन्न होगा, स्वाभाविक रूप से QML सहित QtObject यह सीधे ListView रूप में चलाने के लिए एक मॉडल के रूप में उपयोग किया जा सकता है। आप स्लॉट्स या घोषणात्मक का उपयोग करके इसे गतिशील रूप से आबाद कर सकते हैं, जैसे:

List {
    QtObject { ... }
    QtObject { ... }
    List {
        QtObject { ... }
        QtObject { ... }
    }
}

यह ऑब्जेक्ट स्वामित्व को भी संभालता है, और आप आसानी से इसे घोंसला कर सकते हैं, संक्षेप में एक मिश्रित वृक्ष मॉडल बना सकते हैं - ध्यान दें कि आप क्यूएमएल के ListModel मॉडेल के साथ ऐसा नहीं कर सकते हैं। यदि आप बदलते माता-पिता के खिलाफ बाध्य करना चाहते हैं, तो आप एक parentChanged सिग्नल जोड़ सकते हैं और एक सेटर को लागू कर सकते हैं, जो मेरे मामले में आवश्यक नहीं था।

इसे किसी दृश्य के साथ कैसे उपयोग करें, आप objectName प्रॉपर्टी या एक int type संपत्ति का उपयोग कर सकते हैं, और प्रतिनिधि के लिए Loader उपयोग कर सकते हैं:

Loader {
    width: childrenRect.width
    height: childrenRect.height
}

यदि आप ऑब्जेक्ट नाम का उपयोग करते हैं, तो आप लोडर एक नाम बनाते हैं। name.qml फ़ाइल, यदि आप इंट का उपयोग करते हैं, तो आप Component की एक सरणी बना सकते हैं और स्रोत घटक के रूप में उपयुक्त सूचकांक का उपयोग कर सकते हैं। आप या तो object को Loader की संपत्ति के रूप में उजागर कर सकते हैं और वास्तविक ऑब्जेक्ट UI को parent.object.prop संदर्भित कर सकते हैं या आप setSource(name + ".qml", {"object": object}) का उपयोग कर सकते हैं और उस वस्तु में सीधे ऑब्जेक्ट प्रॉपर्टी, हालांकि setSource केवल बाह्य स्रोतों के साथ काम करेगा, इनलाइन Component एस के साथ नहीं। ध्यान दें कि किसी बाहरी स्रोत के मामले में, object को आगे बढ़ाने के लिए कुछ भी किए बिना भी पहुंचा जा सकता है, हालांकि किसी कारण के लिए यह इनलाइन घटकों के साथ काम नहीं करता है, ऐसे घटकों के साथ ही संभव तरीका है कि इसे एक संपत्ति के रूप में उजागर करना है लोडर


इस समाधान के बारे में क्या?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

यह काम करता है और सच होने के लिए बहुत आसान लग रहा है। क्या कोई कमी है?





c++ qt generics model qtquick2