I'm trying to build a simple qt project with CMake, which contains main.cxx, MainWindow.cxx and MainWindow.hxx. When I try to make install it says fatal error: 'QMainWindow' file not found, but I do added Widgets in the CMakelists.txt.
Here are the codes:
main.cxx:
#include <QApplication>
#include "MainWindow.h"
int main(int argc, char** argv){
QApplication GUI(argc, argv);
MainWindow window;
window.shou();
return GUI.exec();
}
MainWindow.cxx
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
}
MainWindow::~MainWindow()
{
}
MainWindow.hxx
#ifndef MAINWINDOW_HXX_
#define MAINWINDOW_HXX_
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent =0);
~MainWindow();
};
#endif
CMakeLists.txt
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
project(Gui_Window)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt6 COMPONENTS Widgets REQUIRED)
add_library(MAINWINDOW ${CMAKE_CURRENT_SOURCE_DIR}/src/MainWindow.cxx)
add_executable(Gui_Window ${CMAKE_CURRENT_SOURCE_DIR}/app/main.cxx)
target_link_libraries(Gui_Window PUBLIC Qt6::Widgets
MAINWINDOW
)
install(TARGETS Gui_Window DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin)
What should be the problem?
Your MainWindow.cxx file should be included in your CMakeLists.txt file like this:
set(PROJECT_SOURCES MainWindow.cxx)
You'd want main in there too.
As an aside, it's much easier to use Qt Creator and let it generate the CMakeLists.txt file for you.
Related
I have a QObject derived class Expense that I use in QML like this.
// main.qml
Expense {
id: expenseManager
onExpenseCreated: {
// Do something
}
}
The expense class has no UI components, it has some basic Signal and Slots for API communications.
// expense.h
#ifndef EXPENSE_H
#define EXPENSE_H
#include <QObject>
#include <QString>
#include "service.h"
class Expense : public QObject
{
Q_OBJECT
private:
Service service;
void networkError();
bool buttonLock = false;
public:
explicit Expense(QObject *parent = nullptr);
public slots:
void createInvoice(QString item, float amount);
signals:
void expenseCreated();
};
#endif // EXPENSE_H
I have used qmlRegisterType() for registering Expense type in QML. Below is how my main() looks like.
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
......
qmlRegisterType<Expense>("com.kadbyte.expense", 1, 0, "Expense");
........
return app.exec();
}
Everything working perfectly as it used to. But recently I have upgraded my project to QT6 with CMake as the build tool instead of QMake. In the docs I saw that we can use qt_add_qml_module command in CMakeList.txt to register C++ Classes instead of qmlRegisterType(), by adding QML_ELEMENT macro to the QObject class.
But I can't understand how to do this, the documentation doesn't make sense as it uses qmake example (Link to docs) instead of CMake. Below is my CMakeLists.txt file
cmake_minimum_required(VERSION 3.16)
project(Udyan VERSION 0.1 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 6.2 COMPONENTS Quick REQUIRED)
qt_add_executable(appUdyan
main.cpp
expense.h expense.cpp
)
qt_add_qml_module(appUdyan
URI Udyan
VERSION 1.0
QML_FILES qml/main.qml
)
set_target_properties(appUdyan PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
target_compile_definitions(appUdyan
PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(appUdyan
PRIVATE Qt6::Quick)
So how to use qt_add_qml_module to registering QObject class to use in QML?
Note: All the example I have given above is just an MRE and not my complete code.
You just need to add QML_ELEMENT to your QObject-derived Expense class's header and make sure you have moc enabled in your CMakeLists.txt. In application case it doesn't matter if the expense.h/cpp sources are included via qt_add_executable or qt_add_qml_module. I think it's clearer to add them to qt_add_qml_module SOURCES. Then you just import module URI in you QML file. In the example below I'm printing out property value from Expense object in QML.
CMakeLists.txt
set(CMAKE_AUTOMOC ON)
qt_add_qml_module(appUdyan
URI udyan
VERSION 1.0
QML_FILES
main.qml
SOURCES
expense.h
expense.cpp
)
C++
#include <QObject>
#include <QtQml/qqmlregistration.h>
class Expense : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int value READ value NOTIFY valueChanged)
public:
explicit Expense(QObject *parent = nullptr);
int value() const;
signals:
void valueChanged();
private:
int m_value {5};
};
QML:
import QtQuick
import udyan
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Expense {
id: expense
Component.onCompleted: console.log(expense.value)
}
}
I am trying implement rviz2 plugin in ROS2. I have referred user guide from the rviz for the development. Currently there no tutorial available to develop rviz dockable panel in ROS2. Therefore I have referred MoveIt2 and Navigation 2 package for their rviz plugin implementation in ROS2. Here is my code.
demo_widget.h:
#ifndef DEMO_WIDGET_H
#define DEMO_WIDGET_H
#include <QWidget>
#include <memory>
#include <vector>
#include "ui_pointcloud_controller.h"
#ifndef Q_MOC_RUN
#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>
#endif
namespace Ui {
class DemoWidgetUI;
}
namespace prc_demo_panel
{
class DemoWidget : public QWidget
{
Q_OBJECT
public:
DemoWidget(QWidget * parent = 0);
~DemoWidget() override;
public Q_SLOTS:
private Q_SLOTS:
protected:
std::unique_ptr<Ui::DemoWidgetUI> ui_;
private:
rclcpp::Node::SharedPtr _node;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr _publisher;
};
}
demo_widget.cpp:
#include <prc_demo_panel/demo_widget.h>
namespace prc_demo_panel
{
DemoWidget::DemoWidget(QWidget *parent): QWidget(parent), ui_(new Ui::DemoWidgetUI)
{
ui_->setupUi(this);
connect(ui_->pushButton_1, &QPushButton::clicked, this, &DemoWidget::buttonOne);
connect(ui_->pushButton_2, &QPushButton::clicked, this, &DemoWidget::buttonTwo);
auto options = rclcpp::NodeOptions().arguments({"--ros-args --remap __node:=dialog_action_client"});
_node = std::make_shared<rclcpp::Node>("_", options);
_publisher = _node->create_publisher<std_msgs::msg::String>("gui_control", 10);
}
}
demo_panel.h:
#ifndef DEMO_PANEL_H
#define DEMO_PANEL_H
#include <QVBoxLayout>
#include <rviz_common/panel.hpp>
#include <prc_demo_panel/demo_widget.h>
namespace prc_demo_panel
{
class Demo_widget;
class DemoPanel : public rviz_common::Panel
{
Q_OBJECT
public:
explicit DemoPanel(QWidget * parent = 0);
virtual ~DemoPanel();
void onInitialize() override;
void save(rviz_common::Config config) const override;
void load(const rviz_common::Config &conf) override;
private:
DemoWidget *_widget;
};
}
#endif // RVIZ_PANEL_H
demo_panel.cpp:
#include "prc_demo_panel/demo_panel.h"
#include <QVBoxLayout>
#include <memory>
#include <vector>
#include <utility>
#include <rviz_common/display_context.hpp>
namespace prc_demo_panel
{
DemoPanel::DemoPanel(QWidget * parent) : Panel(parent)
{
_widget = new DemoWidget(parent);
QVBoxLayout * layout = new QVBoxLayout;
layout->addWidget(_widget);
layout->setContentsMargins(10, 10, 10, 10);
setLayout(layout);
}
void DemoPanel::save(rviz_common::Config config) const
{
Panel::save(config);
}
void DemoPanel::load(const rviz_common::Config &conf)
{
Panel::load(conf);
}
void DemoPanel::onInitialize()
{
auto node = getDisplayContext()->getRosNodeAbstraction().lock()->get_raw_node();
}
}
#include <pluginlib/class_list_macros.hpp>
PLUGINLIB_EXPORT_CLASS(prc_demo_panel::DemoPanel, rviz_common::Panel)
CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(prc_demo_panel)
.
.
.
# find dependencies
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Test Concurrent)
set (THIS_PACKAGE_INCLUDE_DEPENDS
rclcpp
class_loader
pluginlib
Qt5
rviz2
rviz_common
rviz_default_plugins
rviz_rendering
rviz_ogre_vendor
)
include_directories(
include
)
# I prefer the Qt signals and slots to avoid defining "emit", "slots",
# etc because they can conflict with boost signals, so define QT_NO_KEYWORDS here
# e.g. http://muddyazian.blogspot.de/2012/04/getting-qt-app-working-with-boost-using.html
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
#set(CMAKE_AUTORCC ON)
#add_definitions(-DQT_NO_KEYWORDS)
# Define source file
set(${PROJECT_NAME}_SRCS
src/demo_panel.cpp
src/demo_widget.cpp
#src/plugin_init.cpp
)
# Define header file
set(${PROJECT_NAME}_HDRS
include/${PROJECT_NAME}/demo_panel.h
include/${PROJECT_NAME}/demo_widget.h
)
# Define ui file
set(${PROJECT_NAME}_UIS
resource/pointcloud_controller.ui
)
message(STATUS "Generate header for ui with rviz2_QT_VERSION: ${rviz2_QT_VERSION}")
qt5_wrap_ui(${PROJECT_NAME}_UIS_H ${${PROJECT_NAME}_UIS})
foreach(header "${${PROJECT_NAME}_HDRS}")
qt5_wrap_cpp(${PROJECT_NAME}_MOCS ${header})
endforeach()
## Add library is needed in order to generate the header file from ui file.
add_library(${PROJECT_NAME} SHARED
${${PROJECT_NAME}_SRCS}
${${PROJECT_NAME}_UIS_H}
${${PROJECT_NAME}_MOCS}
)
ament_target_dependencies(${PROJECT_NAME} ${THIS_PACKAGE_INCLUDE_DEPENDS})
target_include_directories(${PROJECT_NAME} PUBLIC
${Qt5Widgets_INCLUDE_DIRS}
${OGRE_INCLUDE_DIRS}
)
target_link_libraries(${PROJECT_NAME} rviz_common::rviz_common)
# target_include_directories(${PROJECT_NAME} PUBLIC
# $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
# $<INSTALL_INTERFACE:include>
# )
target_compile_definitions(${PROJECT_NAME} PUBLIC "PLUGINLIB__DISABLE_BOOST_FUNCTIONS")
target_compile_definitions(${PROJECT_NAME} PRIVATE "RVIZ_DEFAULT_PLUGINS_BUILDING_LIBRARY")
pluginlib_export_plugin_description_file(rviz_common demo_plugin.xml)
install(
TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
install(
DIRECTORY include/
DESTINATION include
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME})
ament_export_targets(${PROJECT_NAME} HAS_LIBRARY_TARGET)
ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS})
ament_package()
package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>prc_demo_panel</name>
<version>0.0.0</version>
<description>Point Cloud Resolution Control</description>
<maintainer email="vishnu.pbhat93#gmail.com">vishnu</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<build_depend>qtbase5-dev</build_depend>
<exec_depend>libqt5-core</exec_depend>
<exec_depend>libqt5-gui</exec_depend>
<exec_depend>libqt5-opengl</exec_depend>
<exec_depend>libqt5-widgets</exec_depend>
</package>
demo_plugin.xml:
<library path="/libprc_demo_panel" >
<class
name="prc_demo_panel/Demo Panel"
type="prc_demo_panel::DemoPanel"
base_class_type="rviz_common::Panel"
>
<description>
Point Cloud Resolution Control
</description>
</class>
</library>
This package can compile colcon build --symlink-install without any errors and rviz2 is able recognize the plugin. However when I try to add the plugin in rviz2 I am getting following error:
[ERROR] [1634722985.299621064] [rviz2]: PluginlibFactory: The plugin for class 'prc_demo_panel/Demo Panel' failed to load.
Error: Could not find library corresponding to plugin prc_demo_panel/Demo Panel. Make sure the plugin description XML file has the correct name of the library and that the library actually exists.
I am not able to determine what is a mistake that I am doing. I would really appreciate any help.
Thank you
Hey I am sure you have figured out the answer to this, just to make sure that others are able to find the answer easily here is what is causing the issue.
The causing of this error is usually the wrong library path. Library path in demo_plugin.xml should be changed to the ros package name of your ros package ( If you build the whole package as a library. ), such as <library path="prc_demo_panel" >. If your ros package has lots of sublibraries, you should change library name to the name of library which your panel lacated.
I am trying to compile a group Qt project with cmake. It worked until I added the GUI part.
main.cpp:
#include <QtWidgets>
int start_GUI(int argc, char *argv[]){
QApplication a(argc, argv);
Window * window = new Window();
SSConnection *ssc = new SSConnection();
Window window;
return a;
}
cmake:
cmake_minimum_required(VERSION 3.6)
project(client)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES main.cpp)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5WebSockets REQUIRED)
find_package(Qt5Gui REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(client_main ${SOURCE_FILES})
qt5_use_modules(client_main Widgets Gui WebSockets)
output:
undefined reference to `Button::mousePressEvent(QGraphicsSceneMouseEvent*)'
undefined reference to `non-virtual thunk to Button::mousePressEvent(QGraphicsSceneMouseEvent*)'
The Button class is just a QObject to add a button to the screen
Button:
class Button : public QObject , public QGraphicsRectItem
{
Q_OBJECT
public:
Button(QString name, QGraphicsItem * parent = NULL);
void mousePressEvent(QGraphicsSceneMouseEvent * event);
signals:
void clicked();
private:
QGraphicsTextItem * text;
QPushButton * button;
QGraphicsRectItem * rect;
};
Any suggestions on how to fix the linking error
To solve the linker error, implement this method or remove it.
void mousePressEvent(QGraphicsSceneMouseEvent * event);
I have 3 files in my c++/qt project and I'm using CMake. I'm trying to compile it Here are some code:
CMakeLists contains:
cmake_minimum_required(VERSION 3.8)
project(untitled)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_PREFIX_PATH /Users/username/Qt/5.9.2/clang_64/)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
find_package(Qt5Core)
find_package(Qt5Network)
set(SOURCE_FILES main.cpp server.cpp)
add_executable(untitled ${SOURCE_FILES})
target_link_libraries(${PROJECT_NAME} Qt5::Core)
target_link_libraries(${PROJECT_NAME} Qt5::Network)
Main.cpp contains:
#include <iostream>
#include <QCoreApplication>
#include <QtDebug>
#include "server.cpp"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
MyTcpServer server;
return app.exec();
}
and finally server.cpp contains:
#include <QObject>
#include <QTcpSocket>
#include <QTcpServer>
#include "server.moc"
class MyTcpServer : public QObject
{
Q_OBJECT
public:
explicit MyTcpServer(QObject *parent = 0);
public slots:
void slotNewConnection();
void slotServerRead();
void slotClientDisconnected();
private:
QTcpServer * mTcpServer;
QTcpSocket * mTcpSocket;
};
MyTcpServer::MyTcpServer(QObject *parent) : QObject(parent)
{
...
}
void MyTcpServer::slotNewConnection()
{
...
}
void MyTcpServer::slotServerRead()
{
...
}
void MyTcpServer::slotClientDisconnected()
{
mTcpSocket->close();
}
I'm trying to compile my project with CMake, and when I run the CMake, I have this problems :
duplicate symbol __ZN11MyTcpServer18qt_static_metacallEP7QObjectN11QMetaObject4CallEiPPv in:
CMakeFiles/untitled.dir/main.cpp.o
CMakeFiles/untitled.dir/server.cpp.o
...
duplicate symbol __ZN11MyTcpServer16staticMetaObjectE in:
CMakeFiles/untitled.dir/main.cpp.o
CMakeFiles/untitled.dir/server.cpp.o
ld: 13 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Telling me that there is a duplicate symbol.
How to resolve this?
It is better not to use #include for .cpp files. It is good practice to split definition and declaration into different files.
(One exception from this is the private declaration in case of PIMPL pattern.)
In case you want to avoid the split because you have only small pieces of code use a header file and implement your methods within the definition of the class.
In case a library is implemented: Do not install the header file in case your class must not accessible from outside.
The simple fix for your case would be fo#include "server.cpp" from your cpp as mentioned in comments (it is rarely needs to be done and virtually never is a good thing).
And the second thing would be to move the #include "server.moc" line to the end of the cpp file. This is important because this file contains implementation of some member functions injected with Q_OBJECT and the class needs to be defined before implementing member functions outside of its body.
I didn't see where is my problem in my main. I saw a lot of posts and I don't know where my error is. I know it's a problem with the main. I tried to clean and rebuild (qmake -project and qmake) but it still doesn't work.
int main(int ac, char *av[]) {
QApplication app(ac, av);
app.setOrganizationName("Zero");
app.setApplicationName("Gomoku");
IntroState *intro = new IntroState();
if (intro->exec() != QDialog::Accepted)
return 1;
GameEngine engine;
engine.show();
return app.exec();
}
My CMake is:
cmake_minimum_required(VERSION 2.8)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
project(Gomoku)
set(SOURCE_FILES src/core/IntroState.cpp
src/core/GameEngine.cpp
src/core/Arbiter.cpp
src/tests/CoreFirstRuleTests.cpp)
find_package(Qt5Widgets REQUIRED)
include_directories(include/core
include/ai
include/graphic
include/tests
include)
set(HW_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(${HW_HEADER_DIR})
qt5_wrap_cpp(Gomoku_SRC ${HW_HEADER_DIR}/core/GameEngine.h
${HW_HEADER_DIR}/core/IntroState.h)
qt5_wrap_ui(Gomoku_UI
${HW_HEADER_DIR}/ui/dialog.ui)
add_executable(Gomoku ${SOURCE_FILES} ${Gomoku_SRC} ${Gomoku_UI})
qt5_use_modules(Gomoku Widgets)
Don't forget to add your main at the compilation...