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);
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'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.
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 recently tried writing animated wallpaper plugins for KDE4. When I switch among them, the animations get jumbled. That is, the previous animation doesn't stop, and the previous frames and the current frames get mixed together. I embed the frames into each program as image resources. Then I write code like the following for each wallpaper. Can anyone tell me what I'm missing (or if it's just a KDE bug)?
dancingcoffee.h
#ifndef DANCINGCOFFEE_H
#define DANCINGCOFFEE_H
#include <Plasma/Wallpaper>
#include <QPainter>
#include <QTimer>
class DancingCoffee : public Plasma::Wallpaper
{
Q_OBJECT
public:
DancingCoffee(QObject *parent, const QVariantList& args);
void paint(QPainter *painter, const QRectF &exposedRect);
private slots:
void updateBackground();
private:
const int m_frames;
const int m_interval;
int m_frame_number;
QTimer m_timer;
};
#endif // DANCINGCOFFEE_H
dancingcoffee.cpp
#include "dancingcoffee.h"
#include <QImage>
K_EXPORT_PLASMA_WALLPAPER(coffee, DancingCoffee)
DancingCoffee::DancingCoffee(QObject *parent, const QVariantList &args) :
Plasma::Wallpaper(parent, args),
m_frames(2),
m_interval(1000 / m_frames)
{
m_frame_number = 0;
m_timer.setSingleShot(true);
connect(&m_timer, SIGNAL(timeout()), this, SLOT(updateBackground()));
m_timer.start(m_interval);
}
void DancingCoffee::paint(QPainter *painter, const QRectF &exposedRect)
{
QString name = QString(":/images/frame-%1.png").arg(m_frame_number);
QImage image(name);
painter->drawImage(exposedRect, image);
}
void DancingCoffee::updateBackground()
{
++m_frame_number;
m_frame_number %= m_frames;
m_timer.start(m_interval);
emit update(boundingRect());
}
CMakeLists.txt
set(CMAKE_INSTALL_PREFIX /usr)
project(plasma-wallpaper-coffee)
cmake_minimum_required(VERSION 2.8)
find_package(KDE4 REQUIRED)
find_package(KDE4Workspace REQUIRED)
include(KDE4Defaults)
add_definitions(${KDE4_DEFINITIONS})
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES})
set(TARGET plasma_wallpaper_coffee)
set(SRC_LIST dancingcoffee.cpp)
qt4_add_resources(RCC_LIST dancingcoffee.qrc)
kde4_add_plugin(${TARGET} ${SRC_LIST} ${RCC_LIST})
target_link_libraries(${TARGET} ${KDE4_PLASMA_LIBS})
install(TARGETS ${TARGET} DESTINATION ${PLUGIN_INSTALL_DIR})
install(FILES plasma-wallpaper-coffee.desktop DESTINATION ${SERVICES_INSTALL_DIR})
plasma-wallpaper-coffee.desktop
[Desktop Entry]
Name=Coffee
Type=Service
Icon=image-jpeg
ServiceTypes=Plasma/Wallpaper
X-KDE-Library=plasma_wallpaper_coffee
X-KDE-PluginInfo-Author=Chris French
X-KDE-PluginInfo-Email=unclechromedome#gmail.com
X-KDE-PluginInfo-Name=coffee
X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Website=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true
And voila! Animated wallpaper on my desktop. But the wallpapers don't play well together. I have to kill the desktop and restart it when I switch wallpapers, and sometimes that doesn't even work. Sometimes I have to do a hard reboot. It's a pain for me, and unacceptable if I ever distribute the wallpapers.
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...