I am on MacOS and I want to inherit QWidget for a class with QObject :: connect but i have a error:
Undefined symbols for architecture x86_64:
"vtable for Core", referenced from:
Core::Core(QWidget*) in Core.cpp.o
NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [bin/babel] Error 1
make[1]: *** [CMakeFiles/babel.dir/all] Error 2
Core.cpp
** EPITECH PROJECT, 2020
** Babel
** File description:
** Core
*/
#include "../include/Core.hpp"
Core::Core(QWidget *parent) : QWidget(parent)
{
this->text = new QLabel("Pseudo", this);
this->textArea = new QTextEdit(this);
this->button = new QPushButton("Connexion", this);
QObject::connect(this->button, SIGNAL(clicked()), this, SLOT(this->buttonPushed()));
this->button->setGeometry(300, 250, 160, 30);
this->textArea->setGeometry(300, 200, 160, 30);
this->text->setGeometry(335, 175, 160, 30);
}
Core::~Core()
{
}
void Core::buttonPushed()
{
printf("EKIP EKIP EKIP\n");
}
Core.hpp:
/*
** EPITECH PROJECT, 2020
** Babel
** File description:
** Core
*/
#ifndef CORE_HPP_
#define CORE_HPP_
#include <stdio.h>
#include <stdlib.h>
#include <opus/opus.h>
#include <portaudio.h>
#include <QtWidgets>
class Core : public QWidget {
Q_OBJECT
public:
explicit Core(QWidget *parent = nullptr);
~Core();
protected:
private slots:
void buttonPushed();
private:
// QApplication *app;
QPushButton *button;
QLabel *text;
QTextEdit *textArea;
};
#endif /* !CORE_HPP_ */
and Main.cpp
/*
** EPITECH PROJECT, 2020
** Babel
** File description:
** main
*/
#include "../include/Core.hpp"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Core *core = new Core();
core->setGeometry(10, 10, 720, 480);
core->setWindowTitle("Babel");
core->show();
return app.exec();
}
Cmake file:
project(Babel)
cmake_minimum_required(VERSION 2.8.12)
add_definitions("-fPIC")
SET(CMAKE_AUTOMOC ON)
SET(CMAKE_AUTOUIC ON)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core)
find_package(Qt5Network)
file(GLOB_RECURSE BABEL_SRC PATH ./sources/*.cpp)
include_directories(${CMAKE_INCLUDE_PATH})
add_executable(babel ${BABEL_SRC})
target_link_libraries(babel ${CONAN_LIBS} Qt5::Widgets Qt5::Core Qt5::Network)
add the files with Q_OBJECT in the cmake like this for example :
set(SOURCES core.cpp )
qt5_wrap_cpp(SOURCES core.hpp)
add_executable(babel ${SOURCES} ${BABEL_SRC})
Related
I'm using Qt 5.15 with C++17. I have a hierarchy of Widgets like hinted in the title:
MainWindow : QMainWindow
->centralWidget() = QScrollArea
->widget() = QWidget
->layout() = QVBoxLayout
->children() = [
InnerWidget,
InnerWidget,
InnerWidget,
...
]
with InnerWidget.children() = [ QLabel, QLabel ].
I have set InnerWidget::setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum) inside InnerWidget's constructor.
The QLabels inside InnerWidget are initialized with some QPixmap img like
label->setPixmap(img);
label->setFixedSize(img.size());
This all works fine, however now I added an option to zoom all the pixmaps to the MainWindow's menubar. I connect to a signal MainWindow::zoom_inner(.., which itself calls InnerWidget::zoom(.. on every InnerWidget, which resizes the images / labels like this:
auto scaled = img.scaled(img.size() * factor);
label->setPixmap(scaled);
label->setFixedSize(scaled.size());
Finally I update the size of the MainWindow by finding the maximum InnerWidget.frameGeometry().width() (= inner_max_width) and calling
void MainWindow::zoom_inner() {
// ...
setMaximumWidth(inner_max_width + centralWidget()->verticalScrollBar()->width() + 3);
resize(maximumWidth(), size().height());
}
Now after calling MainWindow::zoom_inner(.., the window seems to resize to the correct size, as do the QLabel's inside the InnerWidget's. However, the InnerWidget's themselves do not resize at all, they just stay the same size as before and I have no idea why. I tried calling many combinations of
adjustSize();
update();
layout()->update();
updateGeometry();
on any of the MainWindow,QScrollArea,InnerWidget but nothing happens.. even calling InnerWidget::resize(new_size) has no effect. Clearly InnerWidget::sizeHint() gives the correct value, because the MainWindow size after MainWindow::zoom(.. is correct. So why do the InnerWidget's refuse to resize to fit the QLabel's, even though sizePolicy is set to QSizePolicy::Minimum?
This seems like some sort of misunderstanding on my part so I'm hoping the answer is simple and doesn't need a MWE. I've tried the docs but couldn't find a solution.
Below I add a MWE. For persistance of the question I pasted the code instead of providing a github link. This should build after creating the files and running the build.sh script.
Desired behavior: After choosing a zoom factor and confirming, all InnerWidget's should automatically resize to fit the newly rescaled QPixmap's/QLabel's.
Issue: The InnerWidget's never change size, even when calling .resize(.. on them, why?
MWE
include/NestedWidgetResizeIssue_MWE.hh
#include <vector>
#include <QtGui/QPixmap>
#include <QtWidgets/QAction>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenu>
#include <QtWidgets/QScrollArea>
#include <QtWidgets/QWidget>
#include <QtWidgets/QFrame>
class InnerWidget : public QFrame {
QPixmap m_img_1;
QPixmap m_img_2;
QLabel* m_label_1;
QLabel* m_label_2;
static QPixmap create_black_img();
static void update_img_label(const QPixmap& img, QLabel* label);
static void init_img_label(QPixmap& img, QLabel*& label);
static void zoom_img_label(double factor, QPixmap& img, QLabel* label);
public:
InnerWidget();
void zoom(double factor);
};
// --------------------------------------------------
class MainWindow : public QMainWindow {
Q_OBJECT
QMenu* m_edit_menu;
QAction* m_zoom_action;
QScrollArea* m_central;
QWidget* m_main;
std::vector<InnerWidget*> m_entries;
void update_max_width();
private slots:
void zoom_inner();
public:
MainWindow();
};
src/NestedWidgetResizeIssue_MWE.cc
#include <cmath>
#include <QtGui/QPainter>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QScrollBar>
#include <NestedWidgetResizeIssue_MWE.hh>
InnerWidget::InnerWidget() {
setFrameShape(QFrame::Panel);
auto* layout = new QHBoxLayout{};
init_img_label(m_img_1, m_label_1);
init_img_label(m_img_2, m_label_2);
layout->addWidget(m_label_1);
layout->addWidget(m_label_2);
setLayout(layout);
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
adjustSize();
}
QPixmap InnerWidget::create_black_img() {
QPixmap img(400, 400);
QPainter p(&img);
img.fill(QColor::fromRgb(0, 0, 0));
return img;
}
void InnerWidget::update_img_label(const QPixmap& img, QLabel* label) {
label->setPixmap(img);
label->setFixedSize(img.size());
}
void InnerWidget::init_img_label(QPixmap& img, QLabel*& label) {
img = create_black_img();
label = new QLabel{};
update_img_label(img, label);
}
void InnerWidget::zoom_img_label(double factor, QPixmap& img, QLabel* label) {
img = img.scaled(img.size() * factor);
update_img_label(img, label);
}
void InnerWidget::zoom(double factor) {
zoom_img_label(factor, m_img_1, m_label_1);
zoom_img_label(factor, m_img_2, m_label_2);
}
// --------------------------------------------------
MainWindow::MainWindow() :
m_edit_menu{ menuBar()->addMenu("&Edit") },
m_zoom_action{ new QAction{ "&Zoom" } },
m_central{ new QScrollArea },
m_main{ new QWidget } {
connect(m_zoom_action, &QAction::triggered, this, &MainWindow::zoom_inner);
m_edit_menu->addAction(m_zoom_action);
auto* layout = new QVBoxLayout{};
layout->setContentsMargins(0, 0, 0, 0);
for (int i = 0; i < 2; ++i) {
auto* entry = new InnerWidget;
m_entries.push_back(entry);
layout->addWidget(entry);
}
m_main->setLayout(layout);
m_central->setWidget(m_main);
setCentralWidget(m_central);
show();
update_max_width();
resize(maximumWidth(), 500);
}
void MainWindow::zoom_inner() {
bool confirm = false;
auto factor = QInputDialog::getDouble(this, "Zoom images", "Factor", 1.0, 0.1, 2.0, 2, &confirm, Qt::WindowFlags{}, 0.05);
if (!confirm) return;
for (auto* e : m_entries) {
e->zoom(factor);
e->adjustSize();
}
update_max_width();
resize(maximumWidth(), size().height());
}
void MainWindow::update_max_width() {
int max_entry_width = 0;
for (const auto* e : m_entries)
max_entry_width = std::max(max_entry_width, e->width());
setMaximumWidth(max_entry_width + m_central->verticalScrollBar()->width() + 3);
}
src/main.cc
#include <QtWidgets/QApplication>
#include <NestedWidgetResizeIssue_MWE.hh>
int main(int argc, char** argv) {
QApplication app(argc, argv);
MainWindow win;
return app.exec();
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(Test)
add_compile_options(-Wall -Wextra -pedantic)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
cmake_policy(SET CMP0100 NEW)
find_package(Qt5 COMPONENTS Widgets REQUIRED)
include_directories(include)
add_executable(Test
src/main.cc
include/NestedWidgetResizeIssue_MWE.hh
src/NestedWidgetResizeIssue_MWE.cc
)
target_link_libraries(Test
Qt::Widgets
)
install(TARGETS
Test
DESTINATION "${CMAKE_SOURCE_DIR}/bin")
build.sh
#!/bin/bash
mkdir -p build bin
cd build
cmake ..
make
make install
I'm new to Qt and I'm trying to do a simple VoIP application.
I use CMake and conan to get all packages and build the application. If I place all Qt related class headers and sources files on the same directory, I can compile without any problem, but when I move the header files to another directory I found linker problems. I don't know why because in CMakeLists.txt I declare the include directories (and is working fine for others classes not related to Qt), I feel it's something related to autoMOC.
Project structure
|--include
| |
| |--client
| | |
| | |--views
| | | |
| | | |--loginwindow.ui
| | | |--ui_loginwindow.h
| | | |--mainwindow.ui
| | | |--ui_mainwindow.h
| | |--Client.h
| | |--loginWindow.h
| | |--mainWindow.h
| | |--Profile.h
| |--common
| |
| |--server
| |
|--src
| |
| |--client
| | |
| | |--Controller
| | |--Model
| | |--View
| | | |
| | | |--loginWindow.cpp
| | | |--mainWindow.cpp
| |--common
| |
| |--server
| |
I've ommited some directories content because the problem is not found there.
CMakeLists.txt
if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the source code and call cmake from there")
endif ()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR})
project(babel)
cmake_minimum_required(VERSION 3.17.4)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions("-fPIC")
if(MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
else()
set(STANDARD_UNIX_CXX_FLAGS "-Wall -g3 -Wextra -Wfatal-errors")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STANDARD_UNIX_CXX_FLAGS}")
endif()
if (EXISTS ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
else()
message(FATAL_ERROR "No conanbuildinfo.cmake file found")
endif()
conan_basic_setup(KEEP_RPATHS)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt5OpenGL CONFIG REQUIRED)
find_package(Qt5Widgets CONFIG REQUIRED)
find_package(Qt5Core CONFIG REQUIRED)
find_package(Qt5Gui CONFIG REQUIRED)
find_package(Qt5Network CONFIG REQUIRED)
find_package(portaudio REQUIRED)
find_package(Opus REQUIRED)
find_package(asio REQUIRED)
file(
GLOB_RECURSE
SOURCES_CLIENT
${PROJECT_SOURCE_DIR}/src/*.cpp
${PROJECT_SOURCE_DIR}/src/client/*.cpp
${PROJECT_SOURCE_DIR}/include/client/*.hpp
${PROJECT_SOURCE_DIR}/include/client/*.ui
${PROJECT_SOURCE_DIR}/resources.qrc
)
file(
GLOB_RECURSE
SOURCES_SERVER
${PROJECT_SOURCE_DIR}/src/server/*.cpp
)
file(
GLOB_RECURSE
SOURCES_COMMON
${PROJECT_SOURCE_DIR}/src/common/*.cpp
${PROJECT_SOURCE_DIR}/include/common/*.hpp
)
add_executable(babel_client ${SOURCES_CLIENT} ${SOURCES_COMMON})
install(TARGETS babel_client DESTINATION ${PROJECT_SOURCE_DIR}/bin)
target_link_libraries(
babel_client
Qt5::Widgets
Qt5::Network
Qt5::OpenGL
Qt5::Core
Qt5::Gui
opus
portaudio
)
target_include_directories(
babel_client PRIVATE
${CONAN_INCLUDE_LIBS}
${PROJECT_SOURCE_DIR}/include/client
${PROJECT_SOURCE_DIR}/include/client/views
${PROJECT_SOURCE_DIR}/include/common
)
loginWindow.h
#ifndef LOGINWINDOW_H
#define LOGINWINDOW_H
#include <QMainWindow>
#include "mainWindow.h"
#include "views/ui_loginwindow.h"
#include <QKeyEvent>
QT_BEGIN_NAMESPACE
namespace Ui { class LoginWindow; }
QT_END_NAMESPACE
class LoginWindow : public QMainWindow
{
Q_OBJECT
public:
LoginWindow(QWidget *parent = nullptr);
~LoginWindow();
private slots:
void on_loginButton_clicked();
protected:
void keyPressEvent(QKeyEvent *key);
private:
Ui::LoginWindow *ui;
MainWindow *mainWindow;
};
#endif // LOGINWINDOW_H
loginWindow.cpp
#include "loginWindow.h"
#include <QPixmap>
LoginWindow::LoginWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::LoginWindow)
{
int picWidth;
int picHeight;
ui->setupUi(this);
QPixmap pix(":/resources/babel.png");
picWidth = ui->babelPicture->width();
picHeight = ui->babelPicture->height();
ui->babelPicture->setPixmap(pix.scaled(picWidth, picHeight, Qt::KeepAspectRatio));
ui->statusbar->addPermanentWidget(ui->statusText);
ui->loginButton->setDefault(true);
}
LoginWindow::~LoginWindow()
{
delete (ui);
}
void LoginWindow::keyPressEvent(QKeyEvent *key)
{
if (key->key() == Qt::Key_Return)
on_loginButton_clicked();
}
void LoginWindow::on_loginButton_clicked()
{
QString user = ui->userField->text();
QString pass = ui->passwordField->text();
if (user == "test" && pass == "test") {
ui->statusText->setText("Correct login");
this->hide();
mainWindow = new MainWindow(this);
mainWindow->show();
} else{
ui->statusText->setText("Inorrect login");
}
}
mainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "views/ui_mainwindow.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainWindow.cpp
#include "mainWindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
Linker output
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/loginWindow.cpp.o: in function `LoginWindow::~LoginWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/loginWindow.cpp:20: undefined reference to `vtable for LoginWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/loginWindow.cpp.o: in function `non-virtual thunk to LoginWindow::~LoginWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/loginWindow.cpp:23: undefined reference to `vtable for LoginWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/loginWindow.cpp.o: in function `LoginWindow::LoginWindow(QWidget*)':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/loginWindow.cpp:6: undefined reference to `vtable for LoginWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/mainWindow.cpp.o: in function `MainWindow::MainWindow(QWidget*)':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/mainWindow.cpp:5: undefined reference to `vtable for MainWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/mainWindow.cpp.o: in function `MainWindow::~MainWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/mainWindow.cpp:10: undefined reference to `vtable for MainWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/mainWindow.cpp.o: in function `non-virtual thunk to MainWindow::~MainWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/mainWindow.cpp:13: undefined reference to `vtable for MainWindow'
As I've said on the introduction, if I place xxWindow.h and xxWindow.cpp on the same folder, the problem disappears, but when I place them on the include directory it doesn't link. I'm not a cmake expert, but it seems right to me. Any hint about the solution?
Okay, I achieved to solve the problem.
In order to make AUTOUIC function correctly, we have to add all the headers that includes ui_*.h to the sources target and not only to the include path. This way automoc will correctly process the headers and it will be correctly linked.
In my case it is like this:
file(
GLOB_RECURSE
SOURCES_CLIENT
${PROJECT_SOURCE_DIR}/src/*.cpp
${PROJECT_SOURCE_DIR}/src/client/*.cpp
${PROJECT_SOURCE_DIR}/include/client/*.h
${PROJECT_SOURCE_DIR}/include/client/resources/resources.qrc
)
Also, remember to set CMAKE_AUTOUIC_SEARCH_PATHS (cmake version >3.9) with the path where .ui files are located, if not, there will be an AutoUic error.
I hope this will help somebody in the future.
I can't figure out how to link FTDI library in my Qt project. I copied ftd2xx.h file to my project directory. The file I want to link is dll: ftd2xx.lib which is stored in F:\Workspace\qt\libs\ftdi\amd64
I get error:
release/testftdi.o:testftdi.cpp:(.text+0x6f8): undefined reference to `_imp__FT_Open#8'
collect2.exe: error: ld returned 1 exit status
I have QtWidget application with one PushButton:
TestFtdi.pro file:
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = TestFtdi
TEMPLATE = app
LIBS += -L"F:\Workspace\qt\libs\ftdi\amd64" -lftd2xx
INCLUDEPATH += f:/Workspace/qt/libs/ftdi/amd64
SOURCES += main.cpp\
testftdi.cpp
HEADERS += testftdi.h
FORMS += testftdi.ui
main.cpp file:
#include "testftdi.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestFtdi w;
w.show();
return a.exec();
}
testftdi.h file:
#ifndef TESTFTDI_H
#define TESTFTDI_H
#include <QMainWindow>
namespace Ui {
class TestFtdi;
}
class TestFtdi : public QMainWindow
{
Q_OBJECT
public:
explicit TestFtdi(QWidget *parent = 0);
~TestFtdi();
private slots:
void on_pushButton_clicked();
private:
Ui::TestFtdi *ui;
};
#endif // TESTFTDI_H
testftdi.cpp file:
#include "testftdi.h"
#include "ui_testftdi.h"
#include <QDebug>
#include "windows.h"
#include "ftd2xx.h"
TestFtdi::TestFtdi(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::TestFtdi)
{
ui->setupUi(this);
}
TestFtdi::~TestFtdi()
{
delete ui;
}
void TestFtdi::on_pushButton_clicked()
{
FT_HANDLE ftHandle;
FT_STATUS ftStatus;
ftStatus = FT_Open(0, &ftHandle);
if(ftStatus != FT_OK) { // FT_Open failed
qDebug() << "FT_Open failed";
}
}
The compiler command looks in this situation like this:
g++ -Wl,-s -Wl,-subsystem,windows -mthreads -o release\TestFtdi.exe release/main.o release/testftdi.o release/moc_testftdi.o -lmingw32 -LC:/Qt/5.5/mingw492_32/lib -lqtmain -lshell32 -LF:\Workspace\qt\libs\ftdi\Static\amd64 -lftd2xx -lQt5Widgets -lQt5Gui -lQt5Core
Could you help me with this?
My guess is, compiler might be looking for ftd2xx rather than ftd2xx.lib (file name is ftd2xx.lib.dll, right?). Have you tried changing the LIBS line to
LIBS += -L"F:\Workspace\qt\libs\ftdi\amd64" -lftd2xx.lib
I have a C++/Qt application which loads a plugin (.dll/.so) using QPluginLoader facilities.
This plugin is basically an embedded python interpreter which allows to inspect Qt objects in the main application via the PyQt4 module.
Problem is the command PyQt4.QtCore.QCoreApplication.instance(), executed from the plugged-in python interpreter, returns None even though the QCoreApplication instance has been created by the C++ application.
This is only on windows in debug mode.
On linux or on release mode on windows, the command PyQt4.QtCore.QCoreApplication.instance() correctly returns the QCoreApplication instance that was created by the C++ application.
Following is some minimalist code showing the problem.
When compiled in release mode:
$ ./a.out
1+1
2
import PyQt4
import PyQt4.QtCore
PyQt4.QtCore.QCoreApplication.instance()
<PyQt4.QtCore.QCoreApplication object at 0x00C69198>
=> Ok
When compiled in debug mode:
$ ./a.out
import PyQt4
import PyQt4.QtCore
PyQt4.QtCore.QCoreApplication.instance()
=> Not ok (returned None)
File main.cpp
#include <QCoreApplication>
#include <QPluginLoader>
#include <QDebug>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QPluginLoader loader("plugin.dll");
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);
loader.load();
if(!loader.isLoaded()) {
qDebug() << loader.errorString();
return 1;
}
(void)loader.instance();
return app.exec();
}
File plugin.h
#ifndef PLUGIN_H
#define PLUGIN_H
#include <QObject>
#include <Python.h>
class Plugin : public QObject
{
public:
Plugin();
~Plugin();
private:
PyThreadState *m_ts;
};
class InterpInput : public QObject
{
Q_OBJECT
public:
InterpInput(QObject *parent = 0) : QObject(parent) { }
public slots:
void monitorInput();
signals:
void done();
void inputReady();
};
class InterpOutput : public QObject
{
Q_OBJECT
public:
InterpOutput(QObject *parent = 0) : QObject(parent) { }
public slots:
void processLine();
public:
PyThreadState *m_ts;
};
#endif
File plugin.cpp
#include "plugin.h"
#include <QCoreApplication>
#include <QThread>
#include <QtPlugin>
#include <QPluginLoader>
Q_EXPORT_PLUGIN2(Plugin, Plugin)
Plugin::Plugin()
{
Py_Initialize();
PyEval_InitThreads();
InterpInput *in = new InterpInput();
InterpOutput *out = new InterpOutput(this);
in->connect(in, SIGNAL(inputReady()), out, SLOT(processLine()));
in->connect(in, SIGNAL(done()), QCoreApplication::instance(), SLOT(quit()));
QThread *thr = new QThread(this);
in->moveToThread(thr);
thr->connect(thr, SIGNAL(started()), in, SLOT(monitorInput()));
m_ts = PyEval_SaveThread();
out->m_ts = m_ts;
thr->start();
}
Plugin::~Plugin()
{
PyEval_RestoreThread(m_ts);
Py_Finalize();
}
void InterpInput::monitorInput()
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
int ret = PyRun_SimpleString("import sys\nimport code\nic = code.InteractiveConsole()");
assert(ret == 0);
while(true) {
ret = PyRun_SimpleString("line = ic.raw_input()");
if(ret) { break; }
inputReady();
}
done();
PyGILState_Release(gstate);
}
void InterpOutput::processLine()
{
PyEval_RestoreThread(m_ts);
int ret = PyRun_SimpleString("ic.push(line)");
PyRun_SimpleString("sys.stdout.flush()");
PyRun_SimpleString("sys.stderr.flush()");
(void)PyEval_SaveThread();
assert(ret == 0);
}
File Makefile
MOC=/cygdrive/c/Qt/4.8.4/bin/moc
GCC=/cygdrive/c/MinGW/bin/mingw32-g++.exe
FLAGS=-Ic:/Qt/4.8.4/include -Ic:/Qt/4.8.4/include/QtCore -Lc:/Qt/4.8.4/lib -Lc:/Qt/4.8.4/bin -lQtCore4 -Lc:/Python27/libs -lpython27 -Ic:/Python27/include -DQT_NO_DEBUG
#FLAGS=-Ic:/Qt/4.8.4/include -Ic:/Qt/4.8.4/include/QtCore -Lc:/Qt/4.8.4/bin -lQtCored4 -Lc:/Python27/libs -lpython27 -Ic:/Python27/include -g
LIBFLAGS=-shared
all:
$(MOC) plugin.h > plugin_moc.cpp
$(GCC) -o a.out main.cpp $(FLAGS)
$(GCC) -o plugin.dll $(LIBFLAGS) plugin.cpp plugin_moc.cpp $(FLAGS)
The explanation is the following.
In debug version, the C++ application and plugin link to debug Qt libraries (QtCored4.dll, etc.)
Whereas the installed PyQt4 modules (QtCore.pyd, etc.) link to release Qt libraries (QtCore4.dll, etc.)
It follows that the C++ application, and the PyQt4 module, each see a QCoreApplication instance but each see a different one, which lives in a different library (respectively the debug, and the release, version of the Qt library).
It follows that the instance living in the PyQt4 module is not initialized when the C++ application is initialized, and is thus null.
This is verified by compiling the PyQt4 modules specifying that they should be linked to the debug Qt libraries: the problem then disappears as the debug-mode C++ application and the PyQt4 modules link to the same Qt library.
I am using the combination mentioned in the title while trying to run the window layout example from the qt tutorials. The main looks like this:
#include <QtGui>
int main(int argc, char **argv) {
QApplication app(argc, argv);
QWidget window;
QLabel *label = new QLabel(QApplication::translate("windowlayout", "Name:"));
QLineEdit *lineEdit = new QLineEdit();
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(label);
layout->addWidget(lineEdit);
window.setLayout(layout);
window.setWindowTitle(
QApplication::translate("windowlayout", "Window layout"));
window.show();
return app.exec();
}
And the CMakeLists.txt like this:
PROJECT(test)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0)
FIND_PACKAGE(Qt4 REQUIRED)
INCLUDE_DIRECTORIES(${QT_INCLUDE_DIR} ${QT_QTCORE_INCLUDE_DIR} ${QT_QTGUI_INCLUDE_DIR})
SET(test_SRCS main.cc)
QT4_AUTOMOC(${test_SRCS})
ADD_EXECUTABLE(test ${test_SRCS})
TARGET_LINK_LIBRARIES(test ${QT_QTGUI_LIBRARIES} ${QT_QTCORE_LIBRARIES})
The building and compilation work properly but when I run the application, it always show a command prompt. How do I avoid that?
You need to tell CMake that you want a GUI application:
# GUI Type
if(WIN32)
set(GUI_TYPE WIN32)
endif(WIN32)
if(APPLE)
set(GUI_TYPE MACOSX_BUNDLE)
endif(APPLE)
ADD_EXECUTABLE(test ${GUI_TYPE} ${test_SRCS})
Note that when you are compiling on Windows, this will change the program entry from main() to WinMain(), so you will need to modify your sources as well.
Here's what I usually do:
#ifdef _WIN32
class Win32CommandLineConverter;
int CALLBACK WinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, LPSTR /* lpCmdLine */, int /* nCmdShow */)
{
Win32CommandLineConverter cmd_line;
return main(cmd_line.argc(), cmd_line.argv());
}
class Win32CommandLineConverter {
private:
std::unique_ptr<char*[]> argv_;
std::vector<std::unique_ptr<char[]>> storage_;
public:
Win32CommandLineConverter()
{
LPWSTR cmd_line = GetCommandLineW();
int argc;
LPWSTR* w_argv = CommandLineToArgvW(cmd_line, &argc);
argv_ = std::unique_ptr<char*[]>(new char*[argc]);
storage_.reserve(argc);
for(int i=0; i<argc; ++i) {
storage_.push_back(ConvertWArg(w_argv[i]));
argv_[i] = storage_.back().get();
}
LocalFree(w_argv);
}
int argc() const
{
return static_cast<int>(storage_.size());
}
char** argv() const
{
return argv_.get();
}
static std::unique_ptr<char[]> ConvertWArg(LPWSTR w_arg)
{
int size = WideCharToMultiByte(CP_UTF8, 0, w_arg, -1, nullptr, 0, nullptr, nullptr);
std::unique_ptr<char[]> ret(new char[size]);
WideCharToMultiByte(CP_UTF8, 0, w_arg, -1, ret.get(), size, nullptr, nullptr);
return ret;
}
};
#endif
I wanted a release build to hide the console but all other builds to show it. To do that use:
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
SET_TARGET_PROPERTIES(MyApp PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
SET_TARGET_PROPERTIES(MyApp PROPERTIES RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
SET_TARGET_PROPERTIES(MyApp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
SET_TARGET_PROPERTIES(MyApp PROPERTIES MINSIZEREL "/SUBSYSTEM:WINDOWS")
endif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
This will introduce the following linker error:
error LNK2019: unresolved external symbol _WinMain#16 referenced in function ___tmainCRTStartup
to fix it link with QT_QTMAIN_LIBRARY in your CMakeLists.txt file
TARGET_LINK_LIBRARIES(MyApp ${QT_LIBRARIES} ${QT_QTMAIN_LIBRARY} ${OTHER_STUFF_TO_LINK})
Now you don't even have to change your sources as was done in the previous solution.
In your CMakeFile with Qt4, you don't need to change your CPP code or to add flags at compile time. Simply use
set(QT_USE_QTMAIN TRUE)
It will automatically link qtmain.lib with your executable. This library defines other common main() signatures, including WinMain().
With this trick, your app can compile with MSVC under Windows, without mofiying your original main() function and without displaying a console when you run your app outside of your IDE (QtCreator for example).
Final resulting CMakeLists.txt can be :
PROJECT(test)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0)
FIND_PACKAGE(Qt4 REQUIRED)
if(WIN32)
SET(QT_USE_QTMAIN TRUE)
endif(WIN32)
INCLUDE(${QT_USE_FILE})
SET(test_SRCS main.cc)
QT4_AUTOMOC(${test_SRCS})
ADD_EXECUTABLE(test ${test_SRCS})
TARGET_LINK_LIBRARIES(test ${QT_LIBRARIES})
Additionnaly, you don't need to include QtCore and QtGui, they are included by default with QT_USE_FILE (for include folders) and QT_LIBRARIES (for libraries).
You only have to set what modules you use, or you don't use (for Gui) :
# Use Network and Sql modules
set(QT_USE_QTNETWORK TRUE)
set(QT_USE_QTSQL TRUE)
# Do not use Gui module
set(QT_DONT_USE_QTGUI TRUE)
in the pro file, remove the following console definition and it will go
CONFIG += console