Qt resources files with CMake and AUTORCC - c++

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

Related

How to run multiple QTest classes?

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.

Logging from library with spdlog

I am trying to use spdlog in a project involving a library under windows.
I create two loggers. One for the app using the library, one for the library itself.
The library's logger is created from the app but when the library want to add a message, it crash.
Following is an simplified example.
The library
libclass.h
#ifndef LIBCLASS_H
#define LIBCLASS_H
#include <spdlog/spdlog.h>
#ifdef WIN32
# ifdef BUILD_APPLIB_SHARED
# define APPLIB_EXPORT __declspec(dllexport)
# else
# define APPLIB_EXPORT
# endif //BUILD_APPLIB_SHARED
#else
# define APPLIB_EXPORT
#endif // WIN32
class APPLIB_EXPORT LibClass
{
public:
LibClass();
~LibClass();
static std::string loggerName();
void testLog();
private:
std::shared_ptr<spdlog::logger> m_logger;
};
#endif //LIBCLASS_H
libclass.cpp
#include "libclass.h"
const std::string myLoggerName = "lib_logger";
LibClass::LibClass()
{
m_logger = spdlog::get(myLoggerName);
}
LibClass::~LibClass()
{ }
std::string LibClass::loggerName()
{
return myLoggerName;
}
void LibClass::testLog()
{
m_logger->info("Log from library");
}
The application
main.cpp
#include <spdlog/spdlog.h>
#include <applib/libclass.h>
void logtest()
{
auto logger = spdlog::get("app_logger");
logger->info("Log from application");
}
int main(int argc, char *argv[])
{
// create loggers
auto appLogger = spdlog::stdout_logger_mt("app_logger");
auto libLogger = spdlog::stdout_logger_mt(LibClass::loggerName());
// log from app
logtest();
// log from lib
LibClass lc;
lc.testLog();
return 0;
}
Spdlog uses a singleton registry for keeping track of available loggers. You get one instance of that registry per dll and exe though.
When you create the logger in the exe, it is added to the exe's registry but not the dlls. When you use spdlog::get(myLoggerName) in your dll, you are querying the registry of the dll which does not contain the "lib_logger" and thus you are getting a null shared_ptr.
Possible solutions are: create the lib_logger in the lib instead of the exe if you are only using it in the dll anyway, or pass the logger from the exe to the dll and call spdlog::register_logger with it inside the dll before calling spdlog::get(myLoggerName). Then you can use the same logger from both the exe and the dll.
An example how one can register loggers across dll boundaries can be found at https://github.com/gabime/spdlog/wiki/How-to-use-spdlog-in-DLLs
Or an example using your code:
The library
libclass.h
#ifndef LIBCLASS_H
#define LIBCLASS_H
#include <spdlog/spdlog.h>
#ifdef WIN32
# ifdef BUILD_APPLIB_SHARED
# define APPLIB_EXPORT __declspec(dllexport)
# else
# define APPLIB_EXPORT
# endif //BUILD_APPLIB_SHARED
#else
# define APPLIB_EXPORT
#endif // WIN32
class APPLIB_EXPORT LibClass
{
public:
LibClass();
~LibClass();
static std::string loggerName();
void testLog();
private:
std::shared_ptr<spdlog::logger> m_logger;
};
APPLIB_EXPORT void registerLogger(std::shared_ptr<spdlog::logger> logger);
#endif //LIBCLASS_H
libclass.cpp
#include "libclass.h"
const std::string myLoggerName = "lib_logger";
LibClass::LibClass()
{
m_logger = spdlog::get(myLoggerName);
}
LibClass::~LibClass()
{ }
std::string LibClass::loggerName()
{
return myLoggerName;
}
void LibClass::testLog()
{
m_logger->info("Log from library");
}
APPLIB_EXPORT void registerLogger(std::shared_ptr<spdlog::logger> logger)
{
spdlog::register_logger(logger);
}
The application
main.cpp
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <applib/libclass.h>
void logtest()
{
auto logger = spdlog::get("app_logger");
logger->info("Log from application");
}
int main(int argc, char* argv[])
{
// create loggers
auto appLogger = spdlog::stdout_logger_mt("app_logger");
auto libLogger = spdlog::stdout_logger_mt(LibClass::loggerName());
registerLogger(libLogger);
// log from app
logtest();
// log from lib
LibClass lc;
lc.testLog();
return 0;
}
Your example works fine on the current version of spdlog:
$ g++ -std=c++11 main.cpp libclass.cpp -I .
$ ./a.out
[2015-08-24 07:29:04.502] [app_logger] [info] Log from application
[2015-08-24 07:29:04.502] [lib_logger] [info] Log from library
Same when using a shared library:
# CMake config:
project(TEST)
include_directories(.)
add_library(class SHARED libclass.cpp)
target_compile_options(class PUBLIC -std=c++11)
add_executable(main main.cpp)
target_link_libraries(main class)
Results:
$ cmake .
$ make
$ ldd main
...
libclass.so => ...
$ ./main
[2015-08-25 08:57:51.864] [app_logger] [info] Log from application
[2015-08-25 08:57:51.864] [lib_logger] [info] Log from library
Make sure that you link with a DLL runtime on Windows.

How to enable qt dbus marshaling with cmake?

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 :(

Compilation error when moving to Qt5 : 'Type' has not been declared

Use Qt 5.2.1 to compile old Qt Project (maybe created by Qt 4.8):
Lan messenger open source:
#ifndef QTSINGLEAPPLICATION_H
#define QTSINGLEAPPLICATION_H
#include <QApplication>
class QtLocalPeer;
#if defined(Q_WS_WIN) || defined(Q_OS_WIN32)
# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT)
# define QT_QTSINGLEAPPLICATION_EXPORT
# elif defined(QT_QTSINGLEAPPLICATION_IMPORT)
# if defined(QT_QTSINGLEAPPLICATION_EXPORT)
# undef QT_QTSINGLEAPPLICATION_EXPORT
# endif
# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport)
# elif defined(QT_QTSINGLEAPPLICATION_EXPORT)
# undef QT_QTSINGLEAPPLICATION_EXPORT
# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport)
# endif
#else
# define QT_QTSINGLEAPPLICATION_EXPORT
#endif
class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication
{ Q_OBJECT
public:
QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
QtSingleApplication(const QString &id, int &argc, char **argv);
QtSingleApplication(int &argc, char **argv, Type type);
#if defined(Q_WS_X11)
QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0);
QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
#endif
bool isRunning();
QString id() const;
void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
QWidget* activationWindow() const;
// Obsolete:
void initialize(bool dummy = true)
{ isRunning(); Q_UNUSED(dummy) }
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000);
void activateWindow();
Q_SIGNALS:
void messageReceived(const QString &message);
private:
void sysInit(const QString &appId = QString());
QtLocalPeer *peer;
QWidget *actWin;
};
#endif // QTSINGLEAPPLICATION_H
error: 'Type' has not been declared
I dont know how to fix these error.
Will anyone help?
Thank you.
Quote from commit in Qt repository :
QCoreApplication::Type and QApplication::type() have been removed.
These Qt3 legacy application types did not match the application types available in Qt5.
Use for example qobject_cast instead to dynamically find out the exact application type.
https://qt.gitorious.org/qt/qtbase/commit/553e216d891177ee0c2cea70bbd7f21103fc7795.
As a workaround if you don't specify the type in your QtSingleApplication constructor then you can comment it out and still keep using QtSingleApplication without it.
qtsingleapplication.h:
line 70:
//QtSingleApplication(int &argc, char **argv, Type type);
qtsingleapplication.cpp:
line 176:
/*QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) :
QApplication(argc, argv, type)
{
sysInit();
}*/
I have used QtSingleApplication. It is a separate set of libraries used to control the number of processes from the same executable. Apparently, from a similar thread on Qt forum, it uses several features which have been deprecated or removed in Qt5. What you need to do is replace the version which come with your Lan messenger with the updated version. Actually you may want to submit a patch for Qt5 compatibility.

meta-type information for the Qt namespace

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