Trouble subclassing QSlider



  • I'm trying to subclass QSlider so that it takes a double argument instead of an int for setValue and valueChanged slots/signals. Here's my subclass code:

    @
    class QDSlider : public QSlider
    {
    Q_OBJECT
    public:
    explicit QDSlider():QSlider() {}
    explicit QDSlider( QWidget *parent = 0 ):QSlider( parent ) {}
    explicit QDSlider( Qt::Orientation orientation , QWidget *parent = 0 ):QSlider( orientation , parent ) {}
    signals:
    void valueDChanged( double value )
    {
    int ivalue = value;
    emit setValue( ivalue );
    }
    private slots:
    void setDValue( double value )
    {
    int ivalue = value;
    emit setValue( ivalue );
    }
    };
    @

    And here is the error I get when I try to load the compiled library:

    @
    Error in dyn.load(file, DLLpath = DLLpath, ...) :
    unable to load shared object '/usr/local/lib64/R/library/gsDesignExplorer/libs/libgsdesigngui.so':
    /usr/local/lib64/R/library/gsDesignExplorer/libs/libgsdesigngui.so: undefined symbol: _ZTV8QDSlider
    ERROR: loading failed
    @

    Using c++filt, I can see that this refers to the vtable:

    @
    GSDesignGUI/package> c++filt _ZTV8QDSlider
    vtable for QDSlider
    @

    Do I need to redefine all of the virtual functions in QSlider to get this to work? Or, do I just need to redefine one? Are there other, better solutions to what I'm trying to accomplish? I've been working on this for the past 5 days without any luck, nor has Google been helpful in finding a solution.

    Thanks!!

    Dave H

    [EDIT: code formatting, please use @-tags, Volker]



  • Your class is unlikely to compile. You must not implement a signal! Just declare it in the header file. The actual implementation is done by moc (a Qt tool that parses headers) and if you provide your own, the method is implemented twice.

    It's very likely that due to the compiler error, your lib is not recreated and thus lacks the symbols for your slider subclass.



  • Well, it does compile:

    @
    make[3]: Entering directory /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build' [ 20%] Building CXX object CMakeFiles/gsdesigngui.dir/gsdesign.cpp.o [ 30%] Building CXX object CMakeFiles/gsdesigngui.dir/gsDesignGUI.cpp.o [ 40%] Building CXX object CMakeFiles/gsdesigngui.dir/gsDesignTips.cpp.o [ 50%] Building CXX object CMakeFiles/gsdesigngui.dir/GsRList.cpp.o [ 60%] Building CXX object CMakeFiles/gsdesigngui.dir/main.cpp.o [ 70%] Building CXX object CMakeFiles/gsdesigngui.dir/qrc_images.cpp.o [ 80%] Building CXX object CMakeFiles/gsdesigngui.dir/moc_gsdesign.cpp.o [ 90%] Building CXX object CMakeFiles/gsdesigngui.dir/Rcpp.cpp.o [100%] Building CXX object CMakeFiles/gsdesigngui.dir/moc_Rcpp.cxx.o Linking CXX shared library libgsdesigngui.so make[3]: Leaving directory/home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build'
    [100%] Built target gsdesigngui
    make[2]: Leaving directory /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build' make[2]: Entering directory/home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build'
    make[3]: Entering directory /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build' Linking CXX shared library CMakeFiles/CMakeRelink.dir/libgsdesigngui.so make[3]: Leaving directory/home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build'
    make[2]: Leaving directory `/home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build'
    Install the project...
    -- Install configuration: ""
    -- Installing: /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/inst/libs/./libgsdesigngui.so
    @

    If there was a compiler error, that would have been very helpful. But, there isn't.

    Now, how does this signal creation magic happen? Do I say in my best Picard voice: "Computer. Make a double precision signal for QSlider." and the computer does it? I find it very hard to believe that I can just do:

    @
    class foo:public QSlider
    {
    Q_OBJECT
    signal:
    setValue( double );
    }
    @

    and things will magically work perfectly.

    So, to be helpful, how does one do this as you clearly seem to indicate I'm doing it wrong.

    Dave H

    [EDIT: code formatting, Volker]



  • A short not for the beginning: please put your code snippets and log outputs between two @-signs (one before the first line, one after the last) or use the code button of the editor, this makes the code and logs formatted nicely.

    To your question: Yes Captain, you're right! :)
    You can (and must!) just put that signal declaration into the header. The rest is subject to Qt. For the gory details: You put Q_OBJECT into your header, you add the header file to the list of files that are to be moc'ed. In .pro files all files in the HEADERS variable are scanned. In CMake it might be that you have to add it to some special variable, I'm not sure on this, as I'm not using CMake actively - please check. Then for all classes containing Q_OBJECT in their header, that tool called moc (for Meta Object Compiler) generates the magic stuff, including the implemenation of the signal's method body.

    Ah, your missing compiler error could be based on CMake not catching up that the header file is subject to moc, thus not calling moc on it and no automated implementation code is coming into the way of the linker.



  • Well, just tried doing that and I still get the unresolved symbol error with vtable.

    Here's the new version of the class:

    @
    class QDSlider : public QSlider
    {
    Q_OBJECT
    public:
    explicit QDSlider():QSlider() {}
    explicit QDSlider( QWidget *parent = 0 ):QSlider( parent ) {}
    explicit QDSlider( Qt::Orientation orientation , QWidget *parent = 0 ):QSlider( orientation , parent ) {}
    signals:
    void valueChanged( double );
    private slots:
    void setValue( double );
    };
    @

    I added the file to the list of MOC_SRCS in CMake and let it do its thing. Still no go...

    This is getting pretty frustrating...

    Dave H



  • I was using the wrong macro in CMakelist.txt, so moc was no running. I now have that running and creating the .cxx files which do get compiled and linked. However, I am still getting an unresolved symbol error, but for the exact function moc was supposed to magically generate the definition for:

    @
    make[3]: Entering directory /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build' [ 7%] Generating ui_gsdesign.h [ 15%] Generating moc_gsdesign.cxx [ 23%] Generating moc_QDSlider.cxx [ 30%] Generating qrc_images.cxx Scanning dependencies of target gsdesigngui make[3]: Leaving directory/home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build'
    make[3]: Entering directory /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build' [ 38%] Building CXX object CMakeFiles/gsdesigngui.dir/gsdesign.cpp.o [ 46%] Building CXX object CMakeFiles/gsdesigngui.dir/gsDesignGUI.cpp.o [ 53%] Building CXX object CMakeFiles/gsdesigngui.dir/gsDesignTips.cpp.o [ 61%] Building CXX object CMakeFiles/gsdesigngui.dir/GsRList.cpp.o [ 69%] Building CXX object CMakeFiles/gsdesigngui.dir/main.cpp.o [ 76%] Building CXX object CMakeFiles/gsdesigngui.dir/Rcpp.cpp.o [ 84%] Building CXX object CMakeFiles/gsdesigngui.dir/moc_gsdesign.cxx.o [ 92%] Building CXX object CMakeFiles/gsdesigngui.dir/moc_QDSlider.cxx.o [100%] Building CXX object CMakeFiles/gsdesigngui.dir/qrc_images.cxx.o Linking CXX shared library libgsdesigngui.so make[3]: Leaving directory/home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build'
    [100%] Built target gsdesigngui
    make[2]: Leaving directory /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build' make[2]: Entering directory/home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build'
    make[3]: Entering directory /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build' Linking CXX shared library CMakeFiles/CMakeRelink.dir/libgsdesigngui.so make[3]: Leaving directory/home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build'
    make[2]: Leaving directory /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build' Install the project... -- Install configuration: "" -- Installing: /home/david/workspace/GSDesignGUI/package/gsDesignExplorer/inst/libs/./libgsdesigngui.so make[1]: Leaving directory/home/david/workspace/GSDesignGUI/package/gsDesignExplorer/explorergui-build'
    ** testing if installed package can be loaded
    Error in dyn.load(file, DLLpath = DLLpath, ...) :
    unable to load shared object '/usr/local/lib64/R/library/gsDesignExplorer/libs/libgsdesigngui.so':
    /usr/local/lib64/R/library/gsDesignExplorer/libs/libgsdesigngui.so: undefined symbol: _ZN8QDSlider8setValueEd
    ERROR: loading failed

    • removing ‘/usr/local/lib64/R/library/gsDesignExplorer’
    • restoring previous ‘/usr/local/lib64/R/library/gsDesignExplorer’
      @

    Here is the unmangled symbol:

    @
    GSDesignGUI/package> c++filt _ZN8QDSlider8setValueEd
    QDSlider::setValue(double)
    @

    So, how do I get this slot and signal created?? I was told not to generate this myself, but let moc do it for me. Clearly, moc is not doing that, so how do you get it to work?!?

    Here's a copy of the class as defined in the header file moc is processing:

    @
    class QDSlider : public QSlider
    {
    Q_OBJECT
    public:
    explicit QDSlider():QSlider() {}
    explicit QDSlider( QWidget *parent = 0 ):QSlider( parent ) {}
    explicit QDSlider( Qt::Orientation orientation , QWidget *parent = 0 ):QSlider( orientation , parent ) {}
    signals:
    void valueChanged( double );
    private slots:
    void setValue( double );
    };
    @



  • If you invoke moc manually on your header file, does it generate the needed code? If so (and I see no reason why it should not), you have a problem with your build system. I am not experienced with cmake, so I can't help you with that, but i will guarantee you that Volker is right and you must let moc generate the signals implementation for you.



  • moc does generate the needed code when called by hand.



  • Then your problem is with your project definition in cmake, or with cmake itself somehow. Perhaps you can get better assistance in a cmake specific channel.



  • I'm not quite convinced it's cmake yet. I just did the following:

    @

    moc QDSlider.h > moc_QSlider.cpp
    @

    and then added moc_QSlider.cpp to the list of source files for compilation thinking that maybe the moc file was compiled, but not linked.

    I then get the following error:

    @
    Linking CXX shared library libgsdesigngui.so
    CMakeFiles/gsdesigngui.dir/moc_QDSlider.cxx.o: In function QDSlider::metaObject() const': moc_QDSlider.cxx:(.text+0x0): multiple definition ofQDSlider::metaObject() const'
    CMakeFiles/gsdesigngui.dir/moc_QSlider.cpp.o:moc_QSlider.cpp:(.text+0x0): first defined here
    CMakeFiles/gsdesigngui.dir/moc_QDSlider.cxx.o:(.data.rel.ro+0x0): multiple definition of QDSlider::staticMetaObject' CMakeFiles/gsdesigngui.dir/moc_QSlider.cpp.o:(.data.rel.ro+0x0): first defined here CMakeFiles/gsdesigngui.dir/moc_QDSlider.cxx.o: In functionQDSlider::qt_metacast(char const*)':
    moc_QDSlider.cxx:(.text+0x20): multiple definition of QDSlider::qt_metacast(char const*)' CMakeFiles/gsdesigngui.dir/moc_QSlider.cpp.o:moc_QSlider.cpp:(.text+0x20): first defined here CMakeFiles/gsdesigngui.dir/moc_QDSlider.cxx.o: In functionQDSlider::valueChanged(double)':
    moc_QDSlider.cxx:(.text+0x70): multiple definition of QDSlider::valueChanged(double)' CMakeFiles/gsdesigngui.dir/moc_QSlider.cpp.o:moc_QSlider.cpp:(.text+0x70): first defined here CMakeFiles/gsdesigngui.dir/moc_QDSlider.cxx.o: In functionQDSlider::qt_metacall(QMetaObject::Call, int, void**)':
    moc_QDSlider.cxx:(.text+0xb0): multiple definition of `QDSlider::qt_metacall(QMetaObject::Call, int, void**)'
    CMakeFiles/gsdesigngui.dir/moc_QSlider.cpp.o:moc_QSlider.cpp:(.text+0xb0): first defined here
    collect2: ld returned 1 exit status
    @

    The file moc_QSlider.cpp contained:

    @
    GSDesignGUI/package> cat gsDesignExplorer/src/explorergui/moc_QSlider.cpp
    /****************************************************************************
    ** Meta object code from reading C++ file 'QDSlider.h'
    **
    ** Created:
    ** by: The Qt Meta Object Compiler version 62 (Qt 4.7.1)
    **
    ** WARNING! All changes made in this file will be lost!
    *****************************************************************************/

    #include "../explorergui/QDSlider.h"
    #if !defined(Q_MOC_OUTPUT_REVISION)
    #error "The header file 'QDSlider.h' doesn't include <QObject>."
    #elif Q_MOC_OUTPUT_REVISION != 62
    #error "This file was generated using the moc from 4.7.1. It"
    #error "cannot be used with the include files from this version of Qt."
    #error "(The moc has changed too much.)"
    #endif

    QT_BEGIN_MOC_NAMESPACE
    static const uint qt_meta_data_QDSlider[] = {

    // content:
    5, // revision
    0, // classname
    0, 0, // classinfo
    2, 14, // methods
    0, 0, // properties
    0, 0, // enums/sets
    0, 0, // constructors
    0, // flags
    1, // signalCount

    // signals: signature, parameters, type, tag, flags
    10, 9, 9, 9, 0x05,

    // slots: signature, parameters, type, tag, flags
    31, 9, 9, 9, 0x08,

       0        // eod
    

    };

    static const char qt_meta_stringdata_QDSlider[] = {
    "QDSlider\0\0valueChanged(double)\0"
    "setValue(double)\0"
    };

    const QMetaObject QDSlider::staticMetaObject = {
    { &QSlider::staticMetaObject, qt_meta_stringdata_QDSlider,
    qt_meta_data_QDSlider, 0 }
    };

    #ifdef Q_NO_DATA_RELOCATION
    const QMetaObject &QDSlider::getStaticMetaObject() { return staticMetaObject; }
    #endif //Q_NO_DATA_RELOCATION

    const QMetaObject *QDSlider::metaObject() const
    {
    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
    }

    void QDSlider::qt_metacast(const char _clname)
    {
    if (!_clname) return 0;
    if (!strcmp(_clname, qt_meta_stringdata_QDSlider))
    return static_cast<void
    >(const_cast< QDSlider
    >(this));
    return QSlider::qt_metacast(_clname);
    }

    int QDSlider::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
    {
    _id = QSlider::qt_metacall(_c, _id, _a);
    if (_id < 0)
    return _id;
    if (_c == QMetaObject::InvokeMetaMethod) {
    switch (_id) {
    case 0: valueChanged((reinterpret_cast< double()>(_a[1]))); break;
    case 1: setValue((reinterpret_cast< double()>(_a[1]))); break;
    default: ;
    }
    _id -= 2;
    }
    return _id;
    }

    // SIGNAL 0
    void QDSlider::valueChanged(double _t1)
    {
    void _a[] = { 0, const_cast<void>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
    }
    QT_END_MOC_NAMESPACE
    @

    I'll keep looking at my CMakelist.txt file, but I'm not yet convinced that this is where the problem is.



  • So... try it out (with the same sources) with qmake. If that works ok, then the problem is cmake.



  • setDouble() is a slot, you must implement this manually.

    It's only the signals that must not be implemented manually, but are implemented by moc autogenerated code.



  • Ah! OK, so I implemented my QDSlider slot as follows and connected it to the QDSpinBox as follows, as well.

    qdslider.h
    @
    #define QDSLIDER_H

    #include <QSlider>

    class QDSlider : public QSlider
    {
    Q_OBJECT
    public:
    explicit QDSlider();
    explicit QDSlider( QWidget * );
    explicit QDSlider( Qt::Orientation , QWidget * );
    signals:
    void valueChanged( double );
    private slots:
    void setValue( double );
    };

    #endif
    @

    qdslider.cpp
    @
    #include "qdslider.h"

    QDSlider::QDSlider():QSlider() {}
    QDSlider::QDSlider( QWidget *parent = 0 ):QSlider( parent ) {}
    QDSlider::QDSlider( Qt::Orientation orientation , QWidget *parent = 0 ):QSlider( orientation , parent ) {}

    void QDSlider::setValue( double value )
    {
    int ivalue = value;
    emit QSlider::setValue( ivalue );
    }
    @

    connect code in main program:
    @
    QObject::connect( ui->anlErrorDSpin , SIGNAL( valueChanged( double ) ) ,
    ui->anlErrorHSlider , SLOT( setValue( double ) ) );
    QObject::connect( ui->anlErrorHSlider , SIGNAL( valueChanged( double ) ) ,
    ui->anlErrorDSpin , SLOT( setValue( double ) ) );
    @

    This compiles, links, and loads. The problem is that when I run the program, I get the following error:

    @
    Object::connect: No such slot QSlider::setValue( double )
    Object::connect: (sender name: 'anlErrorDSpin')
    Object::connect: (receiver name: 'anlErrorHSlider')
    Object::connect: No such signal QSlider::valueChanged( double )
    Object::connect: (sender name: 'anlErrorHSlider')
    Object::connect: (receiver name: 'anlErrorDSpin')
    @

    Why is the compiled code looking for QSlider::setValue( double ) when anlErrorHSlider is defined as a QDSlider which does have a setValue( double ) defined? I tried doing the following:

    @
    QObject::connect( ui->anlErrorDSpin , SIGNAL( valueChanged( double ) ) ,
    ui->anlErrorHSlider , SLOT( QDSlider::setValue( double ) ) );
    @

    which resulted in the following error at run time:

    @
    Object::connect: No such slot QSlider::QDSlider::setValue( double )
    Object::connect: (sender name: 'anlErrorDSpin')
    Object::connect: (receiver name: 'anlErrorHSlider')
    @

    What do I have to do to get the right setValue called?!?



  • Figured out why the wrong setValue was being used. the ui_xxx.h file was not being updated due to the way I was building the project. Once I deleted the offending ui_xxx.h file and pointed the include directory to the right location of the correct ui_xxx.h file, things worked better.

    Now to set the signals such that updating the slider changes the spinbox and the reverse...


Log in to reply
 

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