I am new to Dbus, and off course QDBUS. I am trying to replicate the example from the nokia developer -QT forum. I have a xml file, through which I have generated the qdbus interface adaptor.cpp and .h. Now, I am trying to include this file, and build it. However, I get the compilation error. Can you please him to fix this one?
(.text.startup+0x4c):-1: error: undefined reference to `MyDemo::MyDemo(QObject*)'
generated- adaptor.cpp
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp -c DemoIfAdaptor -a demoifadaptor.h:demoifadaptor.cpp com.nokia.Demo.xml
*
* qdbusxml2cpp is Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#include "demoifadaptor.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
* Implementation of adaptor class DemoIfAdaptor
*/
DemoIfAdaptor::DemoIfAdaptor(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
}
DemoIfAdaptor::~DemoIfAdaptor()
{
// destructor
}
void DemoIfAdaptor::SayBye()
{
// handle method call com.nokia.Demo.SayBye
QMetaObject::invokeMethod(parent(), "SayBye");
}
void DemoIfAdaptor::SayHello(const QString &name, const QVariantMap &customdata)
{
// handle method call com.nokia.Demo.SayHello
QMetaObject::invokeMethod(parent(), "SayHello", Q_ARG(QString, name), Q_ARG(QVariantMap, customdata));
}
Generated Adaptor.h
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp -c DemoIfAdaptor -a demoifadaptor.h:demoifadaptor.cpp com.nokia.Demo.xml
*
* qdbusxml2cpp is Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#ifndef DEMOIFADAPTOR_H_1392803889
#define DEMOIFADAPTOR_H_1392803889
#include <QtCore/QObject>
#include <QtDBus/QtDBus>
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;
/*
* Adaptor class for interface com.nokia.Demo
*/
class DemoIfAdaptor: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "com.nokia.Demo")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"com.nokia.Demo\">\n"
" <method name=\"SayHello\">\n"
" <annotation value=\"QVariantMap\" name=\"com.trolltech.QtDBus.QtTypeName.In1\"/>\n"
" <arg direction=\"in\" type=\"s\" name=\"name\"/>\n"
" <arg direction=\"in\" type=\"a{sv}\" name=\"customdata\"/>\n"
" </method>\n"
" <method name=\"SayBye\"/>\n"
" <signal name=\"LateEvent\">\n"
" <arg direction=\"out\" type=\"s\" name=\"eventkind\"/>\n"
" </signal>\n"
" </interface>\n"
"")
public:
DemoIfAdaptor(QObject *parent);
virtual ~DemoIfAdaptor();
public: // PROPERTIES
public Q_SLOTS: // METHODS
void SayBye();
void SayHello(const QString &name, const QVariantMap &customdata);
Q_SIGNALS: // SIGNALS
void LateEvent(const QString &eventkind);
};
#endif
main.cpp file -
#include <QtCore/QCoreApplication>
#include <QVariant>
#include <QDBusAbstractInterface>
#include "demoifadaptor.h"
class MyDemo : public QObject
{
Q_OBJECT
public:
explicit MyDemo(QObject *parent = 0);
public Q_SLOTS:
void SayBye();
void SayHello(const QString &name, const QVariantMap &customdata);
Q_SIGNALS:
void LateEvent(const QString &eventkind);
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyDemo* demo = new MyDemo;
new DemoIfAdaptor(demo);
QDBusConnection connection = QDBusConnection::sessionBus();
bool ret = connection.registerService("com.nokia.Demo");
ret = connection.registerObject("/", demo);
return a.exec();
}
MyDemo.pro file.
#-------------------------------------------------
#
# Project created by QtCreator 2014-02-19T15:30:36
#
#-------------------------------------------------
QT += core
QT -= gui
QT += dbus
TARGET = MyDemo
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp \
demoifadaptor.cpp
HEADERS += \
demoifadaptor.h
Your example presents a declaration of explicit MyDemo(QObject *parent = 0); but doesn't supply it's definition. Thus the linker error. The rest of MyDemo methods must be defined as well.
You also need to run the Meta Object Compiler on the source file to avoid
"vtable..."
errors. Try re-run qmake. Any time you add a new call to the Q_OBJECT macro, you need to run qmake again. The vtables issue you're referring to in comments is directly related to that.
In case error still persist add #include "MyDemo.moc" at the end of main.cpp
Related
I have a subproject where I put all my QTest unit tests and build a stand-alone test application that runs the tests (i.e. I run it from within Qt Creator). I have multiple test classes that I can execute with qExec(). However I don't know what is the proper way to execute multiple test classes.
Currently I do it in this way (MVCE):
tests.pro
QT -= gui
QT += core \
testlib
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
TARGET = testrunner
HEADERS += test_foo.h
SOURCES += main.cpp
main.cpp
#include <QtTest>
#include <QCoreApplication>
#include "test_foo.h"
int main(int argc, char** argv) {
QCoreApplication app(argc, argv);
TestFooClass testFoo;
TestBarClass testBar;
// NOTE THIS LINE IN PARTICULAR.
return QTest::qExec(&testFoo, argc, argv) || QTest::qExec(&testBar, argc, argv);
}
test_foo.h
#include <QtTest>
class TestFooClass: public QObject
{
Q_OBJECT
private slots:
void test_func_foo() {};
};
class TestBarClass: public QObject
{
Q_OBJECT
private slots:
void test_func_bar() {};
};
However the documentation for qExec() says this is the wrong way:
For stand-alone test applications, this function should not be called more than once, as command-line options for logging test output to files and executing individual test functions will not behave correctly.
The other major downside is that there is no single summary for all the test classes, only for individual classes. This is a problem when I have dozens of classes that each have dozens of tests. To check if all tests passed I have to scroll up to see all the "Totals" of what passed/failed of each class, e.g.:
********* Start testing of TestFooClass *********
PASS : TestFooClass::initTestCase()
PASS : TestFooClass::test_func_foo()
PASS : TestFooClass::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of TestFooClass *********
********* Start testing of TestBarClass *********
PASS : TestBarClass::initTestCase()
PASS : TestBarClass::test_func_bar()
PASS : TestBarClass::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of TestBarClass *********
I'm also surprised my qExec() || qExec() works considering that the documentation says if a test failed qExec() returns a non-zero value, which should mean all the following qExec() calls wouldn't happen, but this seems not to be the case.
What is the proper way to run multiple test classes? And so that I can see at a glance if any of the hundreds of unit tests I have have failed.
I once found a nice solution using a plain Qt project (no TEMPLATE = subdirs) which uses a macro approach for creating the main function and automatic registering of all test classes (macro, too) with only a simple helper header file.
Here is a sample test class (only the relevant header file):
#ifndef FOOTESTS_H
#define FOOTESTS_H
#include "AutoTest.h"
class FooTests : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void test1();
void test2();
void cleanupTestCase();
};
DECLARE_TEST(FooTests)
#endif // FOOTESTS_H
and the main, which consumes every test class created this way:
#include "AutoTest.h"
TEST_MAIN
The code of AutoTest.h:
#ifndef AUTOTEST_H
#define AUTOTEST_H
#include <QTest>
#include <QList>
#include <QString>
#include <QSharedPointer>
namespace AutoTest
{
typedef QList<QObject*> TestList;
inline TestList& testList()
{
static TestList list;
return list;
}
inline bool findObject(QObject* object)
{
TestList& list = testList();
if (list.contains(object))
{
return true;
}
foreach (QObject* test, list)
{
if (test->objectName() == object->objectName())
{
return true;
}
}
return false;
}
inline void addTest(QObject* object)
{
TestList& list = testList();
if (!findObject(object))
{
list.append(object);
}
}
inline int run(int argc, char *argv[])
{
int ret = 0;
foreach (QObject* test, testList())
{
ret += QTest::qExec(test, argc, argv);
}
return ret;
}
}
template <class T>
class Test
{
public:
QSharedPointer<T> child;
Test(const QString& name) : child(new T)
{
child->setObjectName(name);
AutoTest::addTest(child.data());
}
};
#define DECLARE_TEST(className) static Test<className> t(#className);
#define TEST_MAIN \
int main(int argc, char *argv[]) \
{ \
return AutoTest::run(argc, argv); \
}
#endif // AUTOTEST_H
All credits goes to Rob Caldecott.
Solution:
Add resource files in the add_executable() statement
Problem
(not in the add_library())
Fail to set main window icon.
Notes:
When I don't use AUTORCC I run into some compilation problems:
QtCore/qglobal.h: no such file or directory. But, I do prefer AUTORCC as a more modern CMake approach.
Without the AUTORCC (different CMakeLists.txt than the provided) and Qt-4.6.2 the current code worked.
different CMakeLists.txt)
Code
This is a minimized code of my project. Tree:
|- CMakeLists.txt
|- main_window.hpp
|- main_window.cpp
|- main.cpp
|- resources
| - resources.qrc
| - images
| - logo.png
main_window.cpp
#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
};
#endif
main_window.cpp
#include "main_window.hpp"
MainWindow::MainWindow()
{
// i tried ":/images.png", ":/resources/images/logo.png", ":/logo.png"
setWindowIcon(QIcon(":images/logo.png"));
}
main.cpp
#include <QApplication>
#include "main_window.hpp"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setOrganizationName("Organization");
app.setApplicationName("Application Example");
MainWindow mainWin;
mainWin.show();
return app.exec();
}
CMakeLists.txt.
cmake_minimum_required(VERSION 3.1)
project(qt_project)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt4 4.6 REQUIRED)
set(QT_USE_QTGUI TRUE)
set(QT_USE_QTXML TRUE)
include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
// NOTE: it would be more convenient to be able to add the
// resource file here upon the creation of the library
add_library(mylib main_window.cpp)
// SOLVED
// BEFORE: add_executable(qt_test main.cpp)
add_executable(qt_test main.cpp resources/resources.qrc)
target_link_libraries(qt_test
mylib
${QT_LIBRARIES}
)
resources/resources.qrc
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>images/logo.png</file>
</qresource>
</RCC>
Edit
This is the generated qrc_resources.cxx
#include <QtCore/qglobal.h>
static const unsigned char qt_resource_data[] = {
// /users/ddakop/dev/misc/qt/resources/images/logo.png
// ... removed hex data
};
static const unsigned char qt_resource_name[] = {
// images
// ... removed hex data
// logo.png
// ... removed hex data
};
static const unsigned char qt_resource_struct[] = {
// :
0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
// :/images
0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,
// :/images/logo.png
0x0,0x0,0x0,0x12,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,
};
QT_BEGIN_NAMESPACE
extern Q_CORE_EXPORT bool qRegisterResourceData
(int, const unsigned char *, const unsigned char *, const unsigned char *);
extern Q_CORE_EXPORT bool qUnregisterResourceData
(int, const unsigned char *, const unsigned char *, const unsigned char *);
QT_END_NAMESPACE
int QT_MANGLE_NAMESPACE(qInitResources_resources)()
{
QT_PREPEND_NAMESPACE(qRegisterResourceData)
(0x01, qt_resource_struct, qt_resource_name, qt_resource_data);
return 1;
}
Q_CONSTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(qInitResources_resources))
int QT_MANGLE_NAMESPACE(qCleanupResources_resources)()
{
QT_PREPEND_NAMESPACE(qUnregisterResourceData)
(0x01, qt_resource_struct, qt_resource_name, qt_resource_data);
return 1;
}
Q_DESTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(qCleanupResources_resources))
System
CentOS-5, Qt-4.8.6, CMake-3.2.1, gcc-4.8.2
I think you need to link qrc_resources generated file.
I suppose you know the next info:
http://www.cmake.org/cmake/help/latest/manual/cmake-qt.7.html
Where you can see the next line:
add_executable(myexe main.cpp resource_file.qrc)
More info:
http://www.cmake.org/cmake/help/latest/prop_tgt/AUTORCC.html
I'm not sure if this is new or not, but I have figured out a way to add resources to libraries.
In my library's CMakeLists.txt I have the following:
list (APPEND RESOURCES library.qrc)
qt5_add_resources (RCC_SOURCES ${RESOURCES})
...
add_library(library ${RCC_SOURCES} ${SOURCES})
In the library, in one of my "main classes" (for me, this class was the first thing that gets created in the library and the last thing that gets destroyed), I did the following:
class libClass : public QMainWindow {
Q_OBJECT
public:
libClass() : QMainWindow(nullptr, 0) {
Q_INIT_RESOURCE(library);
}
~libClass() {
Q_CLEANUP_RESOURCE(library);
}
};
I'm developing an application in Qt/QML in a Linux environment. As the application grown bigger several issues appeared.
A particular issue is related to a C++ objecttranslator referred inside the QML sources. The project builds correctly and the translator object works for almost all the items in QML. However, in some places, I get the wrong text. Consider the following example:
Text{
x: 1036; y: 108
id: incomingLabel
text: qsTr("Weather") + translator.tr; // referred object
color: "white";
font.pixelSize: 34
}
In the case I got "WeatherUndefined" as text, all appended. My guess is that when this QML text element requires the text attribute, the translator object is not yet created and that causes the tr to be undefined.
This is how I register the C++ class in QML:
Translator translator(app.data(),":/translations");
viewer.rootContext()->setContextProperty("translator", &translator);
It should be noted that the problem does not appear on Windows but only in my Linux environment.
The complete code for the translator class is:
Yes translator.tr is a read only property. The translator code is:
#include "translator.h"
#include <QDebug>
Translator::Translator(QApplication *app, const QString &source, QObject *parent) :
QObject(parent),
m_app(app),
m_source(source)
{
addTranslation("DE", "lang_de_DE.qm");
addTranslation("ES", "lang_es_ES.qm");
addTranslation("FR", "lang_fr_FR.qm");
addTranslation("IT", "lang_it_IT.qm");
}
void Translator::addTranslation(const QString &name, const QString &file)
{
if (!m_translations.contains(name))
m_translations.insert(name, file);
}
QString Translator::tr()
{
return "";
}
void Translator::setSource(const QString &source)
{
m_source = source;
}
void Translator::translate(const QString &language)
{
if (language == "EN")
removeTranslation();
if (m_translations.contains(language))
{
QString langURL = m_translations.value(language).toString();
if (m_qtTranslator.load(langURL, m_source))
{
removeTranslation();
m_app->installTranslator(&m_qtTranslator);
emit languageChanged();
}
}
}
void Translator::removeTranslation()
{
m_app->removeTranslator(&m_qtTranslator);
emit languageChanged();
}
The header file:
#ifndef TRANSLATOR_H
#define TRANSLATOR_H
#include <QObject>
#include <QVariantMap>
#include <QtWidgets/QApplication>
#include <QTranslator>
class Translator : public QObject
{
Q_OBJECT
Q_PROPERTY(QString tr READ tr NOTIFY languageChanged)
public:
explicit Translator(QApplication *app, const QString &source, QObject *parent = 0);
void addTranslation(const QString &name, const QString &file);
void setSource(const QString &source);
Q_INVOKABLE void translate(const QString &language);
QString tr();
signals:
void languageChanged();
private:
QApplication *m_app;
QString m_source;
QVariantMap m_translations;
QTranslator m_qtTranslator;
void removeTranslation();
};
#endif // TRANSLATOR_H
Any ideas? Thank you in advance.
You need to check whether the problem lies in translator or tr.
If translator is not created yet, you will get this error in the console:
ReferenceError: translator is not defined.
And you won't see Weatherundefined in your Text object.
If translator exists but not tr, then translator.tr will return undefined, so you will get this output when calling qsTr("Weather") + translator.tr:
Weatherundefined
I suggest you call some console.log() to pinpoint the problem:
Text{
x: 1036; y: 108
id: incomingLabel
text: {
console.log(translator);
console.log(translator.tr);
return qsTr("Weather") + translator.tr(); // referred object
}
color: "white";
font.pixelSize: 34
}
Also I notice that your translator object is allocated on stack, that could cause problem too.
I am trying to create a very simple example of dbus marshaling, using cmake as the build system. Complete code is found at the end of the question.
The problem I am facing, is how to include the header defining structures for the messages in the generated adapter. The qdbusxml2cpp tool allows passing required includes, but cmake's qt4_add_dbus_adaptor macro doesn't seems to accept more then include header.
The error is :
chatadaptor.moc:59:72: error: invalid use of incomplete type ‘struct MessageData’
I know I could manually add the include, but I want cmake to do it for me.
So, how can I enable dbus marshaling combined with cmake?
I am not sure if it matters, but I haven't found a way to have dbus marshaling even with qmake.
CMakeLists.txt :
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
PROJECT( CustomDbusTypes )
FIND_PACKAGE( Qt4 COMPONENTS QtCore QtGui QtDBUS REQUIRED )
include(${QT_USE_FILE})
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
SET( CustomDbusTypes_SRC
main.cpp
MyMessages.cpp
DbusMessagesReceiver.cpp
)
SET( CustomDbusTypes_HEADERS
MyMessages.hpp
DbusMessagesReceiver.hpp
)
QT4_WRAP_CPP( CustomDbusTypes_MOC ${CustomDbusTypes_HEADERS} )
SET( HEADERS_NEEDED_FOR_DBUS_ADAPTER
DbusMessagesReceiver.hpp
MyMessages.hpp
)
qt4_add_dbus_adaptor( CustomDbusTypes_ADAPTOR_SRC
com.demo.Chat.xml
DbusMessagesReceiver.hpp
DbusMessagesReceiver )
SET( CustomDbusTypes_QT_GENERATED_FILES
${CustomDbusTypes_MOC}
${CustomDbusTypes_ADAPTOR_SRC} )
add_executable( chat ${CustomDbusTypes_SRC} ${CustomDbusTypes_QT_GENERATED_FILES} )
target_link_libraries( chat
${QT_LIBRARIES}
)
main.cpp :
#include <QApplication>
int main( int argc, char* argv[] )
{
QApplication app( argc, argv );
return app.exec();
}
MyMessages.hpp :
#ifndef MYMESSAGES_HPP
#define MYMESSAGES_HPP
#include <QString>
#include <QMetaType>
#include <QtDBus>
struct MessageData
{
QString sender;
QString message;
};
QDBusArgument& operator<<( QDBusArgument & argument, const MessageData & data );
const QDBusArgument& operator>>( const QDBusArgument & argument, MessageData & data );
void registerCommTypes();
Q_DECLARE_METATYPE(MessageData)
#endif
MyMessages.cpp :
#include "MyMessages.hpp"
QDBusArgument &operator<<( QDBusArgument & argument, const MessageData & data )
{
argument.beginStructure();
argument << data.sender << data.message;
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>( const QDBusArgument & argument, MessageData & data )
{
argument.beginStructure();
argument >> data.sender >> data.message;
argument.endStructure();
return argument;
}
void registerCommTypes()
{
qDBusRegisterMetaType<MessageData>();
}
DbusMessagesReceiver.hpp :
#ifndef DBUSMESSAGESRECEIVER_HPP
#define DBUSMESSAGESRECEIVER_HPP
#include <QObject>
#include <QString>
struct MessageData;
class DbusMessagesReceiver : public QObject
{
Q_OBJECT
public:
/// Slots called by the DBUS messaging system when a message arrives
public slots:
void OnMessageData( const MessageData & data );
};
#endif
DbusMessagesReceiver.cpp :
#include "DbusMessagesReceiver.hpp"
#include "MyMessages.hpp"
#include <iostream>
void DbusMessagesReceiver::OnMessageData( const MessageData & data )
{
std::cout << data.sender.toStdString() << std::endl;
}
Finally, I figured out, and it is quite easy.
The problem was that I forward declared the MessageData structure in the header file (in DbusMessagesReceiver.hpp to be precise) that is included in the generated source file.
Once I included the header including the definition of the MessageData structure in this header, the problem disappeared.
So, the solution is to change DbusMessagesReceiver.hpp to this:
#ifndef DBUSMESSAGESRECEIVER_HPP
#define DBUSMESSAGESRECEIVER_HPP
#include <QObject>
#include <QString>
#include "MyMessages.hpp"
class DbusMessagesReceiver : public QObject
{
Q_OBJECT
public:
/// Slots called by the DBUS messaging system when a message arrives
public slots:
void OnMessageData( const MessageData & data );
};
#endif
Pretty smart from cmake developers - too bad it is not documented :(
If you check the qnamespace.h from Qt source code, you get to see something like this:
#ifndef Q_MOC_RUN
namespace
#else
class Q_CORE_EXPORT
#endif
Qt {
#if defined(Q_MOC_RUN)
Q_OBJECT
#endif
#if (defined(Q_MOC_RUN) || defined(QT_JAMBI_RUN))
// NOTE: Generally, do not add Q_ENUMS if a corresponding Q_FLAGS exists.
Q_ENUMS(ScrollBarPolicy FocusPolicy ContextMenuPolicy)
Q_ENUMS(ArrowType ToolButtonStyle PenStyle PenCapStyle PenJoinStyle BrushStyle)
Q_ENUMS(FillRule MaskMode BGMode ClipOperation SizeMode)
Q_ENUMS(BackgroundMode) // Qt3
My interpretation of this code is, that the moc preprocessor is fooled into generating meta-type information for a fake Qt object. How can I access this "fake" meta-object to get, for example, a QMetaEnum for ArrowType and other enums?
The code below does it. The output is:
LeftArrow
#include <QtCore/QTextStream>
#include <QtCore/QMetaEnum>
struct StaticQtMetaObject : public QObject
{
static inline const QMetaObject& get() {return staticQtMetaObject;}
};
int main(int argc, char *argv[])
{
const QMetaObject& mo = StaticQtMetaObject::get();
int index = mo.indexOfEnumerator("ArrowType");
QMetaEnum me = mo.enumerator(index);
Qt::ArrowType arrowType = Qt::LeftArrow;
QTextStream(stdout) << me.valueToKey(arrowType) << endl;
return 0;
}
Courtesy of http://qt-project.org/forums/viewthread/658