CLion and Qt5: tuning qmake - c++

I have a set-up as explained elsewhere, e.g. here. I have Qt5 installed system-wide,
and have the requisite lines in my CMakeLists.txt. My IDE is Clion.
Everything in a simple GUI goes fine until I add Q_OBJECT macro (I want this to connect signals to slots). Now, when I do this, I get the undefined reference to vtable-type error,
that is also found in abundance on the net.
My confusion arises from the fact that some recommend using Qt5-bundled cmake for your project,
which essentially means "just for GUI" I need to change the toolchain. But some actually say nothing about it. What all say is that
Qt runs qmake every time Q_OBJECT is added/deleted
Now, how to capture that in my CMakeLists.txt? -- the relevant part thereof is given below.
I've seenmoc and qmake inside /usr/lib/qt5/bin; so how to communicate this to CLion?
# ----- GUI part -----
# Qt5 inclusion
# The meta object compiler is one of the core functionality of Qt, it reads a C++ header file and if it finds a
# Q_OBJECT macro, it will produces a C++ source file containing meta object code for the class.
# It's the mechanism that allow signal and slots to work.
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# set(CMAKE_PREFIX_PATH $ENV{QT_DIR}/$ENV{QT_VERSION}/gcc_64/lib/cmake)
set(CMAKE_PREFIX_PATH /usr/lib/qt5/bin/)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
# Enable user interface compiler (UIC)
# The user interface compiler is a program that read XML from the .ui file
# generated by Qt Interface Designer and generate C++ code from it.
set(CMAKE_AUTOUIC ON)
if(CMAKE_VERSION VERSION_LESS "3.7.0")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
endif()
set(CMAKE_MODULE_PATH /usr/lib/qt5)
# #see: https://stackoverflow.com/questions/51994603/cmake-qt5-undefined-reference-to-qprinterqprinterqprinterprintermode
SET(QT5_MODULES Widgets PrintSupport)
find_package(Qt5 COMPONENTS ${QT5_MODULES} REQUIRED)
add_subdirectory(${PROJECT_SOURCE_DIR}/extern/qcustomplot)
add_executable(gui
${PROJECT_SOURCE_DIR}/gui/main.cpp
${PROJECT_SOURCE_DIR}/extern/qcustomplot/qcustomplot.cpp)
set_target_properties(gui PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(gui
PUBLIC
Qt5::Core Qt5::Widgets qcustomplot)
Never mind the verbose comments; my initial GUI training being in Java Swing, I found them useful.
EDIT: what helped me was the qt5_wrapper_cpp thing mentioned in
Qt 5 cmake fails with undefined reference to vtable on hello world with inc & src as subdirs

Q_OBJECT macro requires code generation. That's why you got undefined reference exceptions. I don't remember exactly how to configure a cmake project for Qt, but I would recommend reading https://cmake.org/cmake/help/latest/manual/cmake-qt.7.html.
Something like set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC TRUE) should help you.

Related

How to write Qt test for a Qt project

I have read the 5 tutorial from QT-test website. But they are not what I want.
I want to build a project and its unit test part is detached from the main program.
For example:
In main program has a push-botton on widgets and if it was clicked then a Qlabel will show a number.
If I run the test programm, It would mock MouseClicked Event on that button, then verify if the value which shows up on Qlabel is our expectations.
My question is how can I get my test program linked to the main program so that I can access its members? Besides that I want to build the project using CMake, how should I write the CMakeLists for the test project?
---root
|---test
|----CmakeLists.txt
|----test.cpp
|----test.h
|---build
|----main.cpp // only int main function
|----QMainwindows.cpp
|----QMainwindows.h
|----QMainwindows.ui
|----CmakeLists.txt
And the CMakeLists.txt in root
cmake_minimum_required(VERSION 3.5)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
project(myproject VERSION 0.1 LANGUAGES CXX)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
set(PROJECT_SOURCES
main.cpp
QMainwindows.cpp
QMainwindows.h
QMainwindows.ui
)
add_executable(myproject ${PROJECT_SOURCES})
target_link_libraries(myprojectPRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
I am also confused how to access the members from mainwindow in our test. I mean I cant include the ui_mainwindow.h and create an UI_Mainwindow Instance
I found an approach that could answer my question.
In test part I can use findchild<T>("WidgetsName") to get access to ui members of any class.
reference : How do you get a widget's children in Qt?
This may not be a universal solution and if anyone has a better idea, I would be grateful.

How to create a cmake header-only library that depends on external non-header-only target?

How to create a cmake header-only library that depends on external header files? is close but different.
I have a single-header library, MyHeaderLib. In MyHeaderLib/MyHeader.h I have #include <QString>, so anyone doing #include "MyHeaderLib/MyHeader.h" had better have QString in their path (i.e., Qt5Core to CMake, I think(?)) and it they'll need to link to Qt5Core.
What belongs in my CMakeLists.txt for MyHeaderLib? I have
cmake_minimum_required(VERSION 3.12)
add_library(MyHeaderLib INTERFACE)
target_include_directories(MyHeaderLib include/)
# (^ Where include/ contains MyHeaderLib/MyHeader.h)
Anything I try with target_link_libraries(MyHeaderLib requires INTERFACE and if I do target_link_libraries(MyHeaderLib INTERFACE Qt5Core) that doesn't suffice.
Ultimately I got it to work as follows, but I don't understand what is going on:
cmake_minimum_required(VERSION 3.12)
find_package(Qt5Core REQUIRED) # <- Can't be Qt5::Core
add_library(MyHeaderLib INTERFACE)
target_include_directories(MyHeaderLib include/)
# (^ Where include/ contains MyHeaderLib/MyHeader.h)
target_link_libraries(MyHeaderLibrary
INTERFACE
Qt5::Core # <- Can't be Qt5Core
)
I gather the targets with :: in them are aliases, but I'm perplexed why it needs to be exactly like this. Furthermore, I can't find add_library(Qt5::Core ALIAS Qt5Core) anywhere. What is going on? Why do I have to find_package(Qt5Core REQUIRED) and not find_package(Qt5::Core REQUIRED) and why can't target_link_libraries take Qt5Core?
Packages are responsible for defining targets. The Qt maintainers chose to name the package Qt5Core while deciding to define the Qt5::Core target.
Usually the convention with CMake packages is that a package named package-name will define package-name::package-name with maybe other optional targets or subcomponents of package-name::package-name.
As to answer why Qt don't act like this, look inside Qt5CoreConfig.cmake, you'll see this line:
add_library(Qt5::Core SHARED IMPORTED)
Here you go. The file is named Qt5CoreConfig so it needs find_package(Qt5Core), but the target is under the Qt5 namespace as they choose to define it.
This is maybe because Qt5 Also has a general package which you can use components:
find_package(Qt5 REQUIRED COMPONENTS Core)
# Here Qt5::Core kinda make sense.

Can values be set for CMake SOURCE(S) other than using set?

Suppose I have a simple C++ hello-world project with the following CMake script:
cmake_minimum_required(VERSION 3.15)
project(hello)
set(SOURCE main.cpp)
add_executable(${PROJECT_NAME} ${SOURCE})
Now I noticed that PROJECT_NAME is built-in and its value is set from project(*value*) but also SOURCE (and SOURCES) seems to be provided by CMake too.
Are there other ways where SOURCE can be assigned with project source files? Just like the same behavior with PROJECT_NAME. Or is set(SOURCE ...) the intended method.
I'm new to CMake. The SOURCE and SOURCES variables were colored out on my text editor. I'm confused.
Using a SOURCE variable is a common patter in CMake files, but it is not required.
The code above can be written without any variables, it would look something like this:
add_executable(hello main.cpp)
When there are a lot of source files, passing them all to add_executable can be inconvenient. Another alternative is target_sources:
add_executable(hello)
target_sources(hello PRIVATE main.cpp)

Visual studio project for header only library

I'm creating a CMake project whose two main files are:
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(CPP_Algorithms_and_Data_Structures)
set( CMAKE_CXX_STANDARD 11 )
#add_subdirectory(./ElementaryAlgorithms)
add_subdirectory(./ElementaryDataStructures)
#add_subdirectory(./AdvancedDataStructures)
#add_subdirectory(./GraphAlgorithms)
#set(INCLUDE_FOLDERS
# ./
# ./ElementaryAlgorithms
# ./ElementaryDataStructures
# ./AdvancedDataStructures
# ./GraphAlgorithms)
set(INCLUDE_FOLDERS ./ ./ElementaryDataStructures)
set(HEADER_FILES alg-and-ds.h)
set(SRC_FILES main.cpp alg-and-ds.cpp)
add_executable(alg-and-ds ${SRC_FILES} ${HEADER_FILES})
target_include_directories(alg-and-ds PUBLIC ${INCLUDE_FOLDERS})
target_link_libraries(alg-and-ds elementary-data-structures)
#target_link_libraries(alg-and-ds
# graph-algorithms
# elementary-data-structures
# elementary-algorithms
# advanced-data-structures)
and
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(ElementaryDataStructures)
set( CMAKE_CXX_STANDARD 11 )
if(WIN32)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS YES CACHE BOOL "Export all symbols")
endif()
add_library(elementary-data-structures INTERFACE)
target_include_directories(elementary-data-structures INTERFACE ./)
target_sources(elementary-data-structures INTERFACE
"${CMAKE_CURRENT_LIST_DIR}/list.h"
"${CMAKE_CURRENT_LIST_DIR}/list.tcc")
#set_target_properties(elementary-data-structures PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
I'm using these to generate a visual studio solution, however what I would like to do is to generate a separate project for the header only library.
Basically I had a small list library that I converted to an header only library, by using templates, before such change I was able to generate separate visual studio projects but in the same solution, in this case instead I can see something like this:
But what I'd like to see, assuming this is possible is a separate project for the ElementaryDataStructures.
I'm not an expert in CMake and all the setups, but I would be great if you could help me to figure out how to do it.
Update:
Following suggestion on the comment I got a new project in VS, however there's still a tiny bit that bothers me.
In the picture below I can see both alg-and-ds and ElementaryDataStructures_ referencing the same sources. Is there a way to avoid the alg-and-ds project to show such files?
The update CMakeLists.txt
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(ElementaryDataStructures)
set( CMAKE_CXX_STANDARD 11 )
if(WIN32)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS YES CACHE BOOL "Export all symbols")
endif()
add_library(elementary-data-structures INTERFACE)
target_include_directories(elementary-data-structures INTERFACE ./)
target_sources(elementary-data-structures INTERFACE
"${CMAKE_CURRENT_LIST_DIR}/list.h"
"${CMAKE_CURRENT_LIST_DIR}/list.tcc")
add_custom_target(ElementaryDataStructures_ SOURCES ${CMAKE_CURRENT_LIST_DIR}/list.h ${CMAKE_CURRENT_LIST_DIR}/list.tcc)
As far as I know there is no normal way to do it. Only a hackish one. So you create a custom target which will force MSVC to show the project in the solution tree. Something like this:
add_custom_target(${PROJECT_NAME}_ SOURCES ${PROJECT_SOURCES})
Note the underscore in the name: it is there to differentiate it from the name in the add_library command. Of course you need to replace the variables in my example to yours actual ones.
Another solution is to declare static library with stub source file:
file(TOUCH ${CMAKE_BINARY_DIR}/stub.cpp)
add_library(elementary-data-structures STATIC
"${CMAKE_BINARY_DIR}/stub.cpp"
"${CMAKE_CURRENT_LIST_DIR}/list.h"
"${CMAKE_CURRENT_LIST_DIR}/list.tcc"
)
target_include_directories(elementary-data-structures INTERFACE ./)

Qt w/ Cmake: set(QT_USE_QTWEBKIT TRUE) not working

I'm attempting to build a Qt-based application using cmake (It's what Kdevelop gave me). I tried to use a QWebView;
QWebView *webView = new QWebView( this );
webView->load(QUrl("http://google.ca"));
But it failed with Undefined Reference errors...
undefined reference to `QWebView::QWebView(QWidget*)'
undefined reference to `QWebView::load(QUrl const&)'
I looked it up and I needed to add QTWEBKIT to my project, but all the solutions said to add it to my .pro file... And I'm not using .pro. In the QT documentation it said to add "set(QT_USE_QTWEBKIT TRUE)" to my CMAKE file, this is my CMakeLists.txt file now:
#-------------------------------------------------------------------------------
# Corrections Tool CMAKE list
#-------------------------------------------------------------------------------
project(corrections)
# Versioning Requirements
#-------------------------------------------------------------------------------
cmake_minimum_required(VERSION 2.6)
find_package(Qt4 REQUIRED)
# Include QT Librtaries
#-------------------------------------------------------------------------------
set(QT_USE_QTWEBKIT TRUE)
# Set Sources
#-------------------------------------------------------------------------------
set(corrections_SRCS corrections.cpp main.cpp utilities.cpp prettySplash.cpp)
#The Rest
#-------------------------------------------------------------------------------
include_directories(${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
qt4_automoc(${corrections_SRCS})
add_executable(corrections ${corrections_SRCS})
target_link_libraries(corrections ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
install(TARGETS corrections RUNTIME DESTINATION bin)
But I'm still getting the errors, so either I did it wrong, in the wrong place, etc. I've also cleaned out and reconfigured my project several times making sure I wasn't using a bad generated makefile.
How would I either fix my cmake config to actually work, or convert my project to using .pro (with minimum stress & heartache)?
Thank you.
The canonical way to select Qt components in CMake is to specify the them in the find_package call and then to include ${QT_USE_FILE}
FIND_PACKAGE( Qt4 COMPONENTS QtWebKit REQUIRED )
INCLUDE( ${QT_USE_FILE} )
...
TARGET_LINK_LIBRARIES( corrections ${QT_LIBRARIES} )
This already configures the include directories and sets ${QT_LIBRARIES} to contain all relevant Qt libraries (i.e. your selected component and all Qt libraries it depends on).
So you don't need to manually add the libraries by listing them individually as you did in your example.
Edit:
Additional explaination:
The COMPONENTparameter to FIND_PACKAGE actually does the same as your manual call to set QT_USE_WEBKIT. But this variable is only evaluated/used in UseQt4.cmake which is included (and "executed") by the INCLUDEcommand. See CMake documentation of FindQt4 for details.
You do not appear to be linking against all of the Qt libraries. Use ${QT_LIBRARIES} instead of ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} in your target_link_libraries
target_link_libraries(corrections ${QT_LIBRARIES} )