Trying to implement an ObjectPool design pattern with a TabBarLayout



  • I have (one part) of my calculator object working fine as a single instance. My next step is to use a TabBarLayout to quickly compare different sets of equation values; 5 in my case.

    My approach is to create an object pool class with an array of 5 pointers to previously instantiated calcXY objects. As the user swipes left-to-right or selects a TabButton, I would like the displayed QML to get its values from the appropriate calcXY object, the currXYObject being set by a call to the calcXYPool object. Since the public slots are in the individual calcXY objects, both the pool and calculator class are defined as subclasses of QObject:

    @
    class CalcXY : public QObject
    {
    Q_OBJECT
    ...
    @

    I first tried to use the late instantiation "singleton":http://stackoverflow.com/questions/1008019/c-singleton-design-pattern#1008289 pattern. But that give me a slew of compile errors which I wasn't able to resolve. So I reverted back to the simpler template of a Q_OBJECT class, since the class instance is controlled by the following in any case. I'll leave the singleton implementation until later:

    @
    viewer.rootContext()->setContextProperty("calcXYPoolObject", &calcXYPool);
    @

    So then the TabButton event call would be something like this:

    @
    TabButton { tab: aModel; text: "A"; onClicked: calcXYPoolObject.setCurrCalcXY(0) }
    TabButton { tab: bModel; text: "B"; onClicked: calcXYPoolObject.setCurrCalcXY(1) }
    ...
    @

    The CalcXYPool class is quite simple:

    @
    CalcXYPool::CalcXYPool(QObject *parent) :
    QObject(parent)
    {
    int i;

    // Set up instances of CalcXY.
    for (i=0; i<NUMCALCS; i++) {
        calcXYs[i] = new CalcXY((QObject *)0);  // I don't know if this is correct.  I was trying to cast the parent as per the declaration is the .h file
    }
    

    }

    void CalcXYPool::setCurrCalcXY(int calcIndex) {
    currCalcXY = calcXYs[calcIndex];
    }

    CalcXY CalcXYPool::getCurrCalcXY(void) {
    return currCalcXY;
    }
    @

    However, I continue to get errors that I am not able to understand:

    @
    error: no match for 'operator=' in '((CalcXYPool*)this)->CalcXYPool::calcXYs[i] = (operator new(36u), (<statement>, ((CalcXYPool*)<anonymous>)))'
    @

    and the following:

    @
    error: 'QObject::QObject(const QObject&)' is private
    error: within this context
    In member function 'CalcXY CalcXYPool::getCurrCalcXY()':
    @

    Any help that could be provided would be much appreciated. Or if there is a better way of doing what I want to do, I would be grateful for some pointers.

    advTHANKSance.



  • Why are you not using QList<CalcXY*> instead ?
    Do the calcXY constructor like this :

    @class CalcXY : public QObject
    {
    Q_OBJECT
    public:
    CalcXY(QObject * parent = 0);

    }@

    And then :
    @
    for (i=0; i<NUMCALCS; i++) {
    calcXYList.append(new CalcXY);
    }
    @



  • I attempted to do what you suggested, although initially I had:

    @
    public:
    explicit CalcXY(QObject *parent = 0);
    @

    When I use void in the constructor, I get:

    @
    error: return type specification for constructor invalid
    @

    That aside, when I use the QList approach, I get the following error:

    @
    error: no matching function for call to 'QList<CalcXY>::append(CalcXY*)'
    @

    in response to the line:

    @
    calcXYList.append(new CalcXY);
    @

    My CalcXYPool has the following:

    @
    class ScreenRatioCalcPool : public QObject
    {
    Q_OBJECT

    private:
        QList<CalcXY> calcXYList;
        CalcXY   currCalcXY;
    

    @

    I must be missing something obvious?


  • Moderators

    The error message
    @
    error: no matching function for call to 'QList<CalcXY>::append(CalcXY*)'
    @
    tells all.

    Your QList is a list of CalcXY objects. Trying to append "new CalcXY()" is trying to append a pointer to CalcXY (thus the CalcXY*).

    If you look at dridk's example, you'll see that he has a QList<CalcXY*> which is a list of CalcXY pointers, which is what you need if you want to append an object created via new.

    There's a big difference between an object and a pointer to an object. :-)



  • And there is no "void" in the constructor. this is a mistake from me..



  • [quote author="mlong" date="1332871529"]

    If you look at dridk's example, you'll see that he has a QList<CalcXY*> which is a list of CalcXY pointers, which is what you need if you want to append an object created via new.

    [/quote]

    Perfect. I missed that. Thanks.


  • Moderators

    And, I'm guessing that currCalcXY should be a CalcXY* too instead of a CalcXY, or perhaps you should just keep track of an integer which is an index into your QList of CalcXY pointers.

    Then you could simplify things with an accessor method
    @
    CalcXY* CalcXYPool::current()
    {
    if (currCalcXY >= 0 && currCalcXY < calcXYList.count()) {
    return calcXYList.at(currCalcXY);
    } else {
    qWarning("Current CalcXY item is invalid! Returning 0.");
    return 0;
    }
    }
    @
    (Brain to terminal; ymmv)



  • That is much cleaner! Ultimately, I need to get the QML to point to the currCalcXY.

    So something like this I am guessing?

    @
    // Ignore the exitingField toggle. I'll incorporate dridk's template shortly.
    onActiveFocusChanged: {calcXYObject.toggleExitingField();
    calcXYPoolObject.current().setX(x.text)}
    @

    to set the x-value of the current calculator.

    Still in the process of mapping my decades old experience in NeXTSTEP programming to Qt (and C++) ;).

    Cheers.



  • As expected, I am still short on some QML subtleties ;).

    Using the above example I get the following error:

    @
    Result of expression 'calcXYPoolObject.getCurrCalc().setX' [undefined] is not a function.
    @

    So I am guessing that I need to cast 'calcXYPoolObject.getCurrCalc()' to (CalcXY *) because QML doesn't know the interface to the CalcXY class, but how to do that in QML. Any suggestions? I hope that I don't need to promote the entire interface of CalcXY into CalcXYPool.

    BTW, I have confirmed using qDebug() that both the calcXYList is populated, and that in the snippet provided by mlong that we are returning a valid calcXY from the calcXYList.

    Thanks.



  • So, I have tried another approach, which I thought might work, but still no success.

    Following mlong's suggestion, I only keep track of the integer value of the currCalcXY. But since I don't know how to cast back to the calcXYObject in QML, I am trying to set/reset the calcXYObject inside C++ (but not in main). That way the QML can remain simple.

    @
    // Store the pointer to the viewer so that CalcXYPool can setContextProperty
    void CalcXYPool::setAppViewer(QmlApplicationViewer *viewer) {
    appViewer=viewer;
    }
    @

    @
    void CalcXYPool::setCurrCalcXY(int calcIndex) {
    currCalcXY = calcIndex;
    (*appViewer).rootContext()->setContextProperty("calcXYObject", calcXYList.at(currCalcXY));
    }
    @

    In main, I call:

    @
    QmlApplicationViewer viewer;

    calcXYPool.setAppViewer(&viewer);
    

    @

    and in the QML, I can just go back to:

    @
    // Now we should be able to directly reference calcXYObject rather
    // than having to cast calcXYObjectPool.current()
    onActiveFocusChanged: {calcXYObject.toggleExitingField();
    calcXYObject.setX(x.text)}
    @

    I thought that it was a good idea. Unfortunately, it didn't work :(.

    @
    calcxy.qml:52: ReferenceError: Can't find variable: calcXYObject
    @

    Any thoughts or suggestions?

    Thanks.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.