Markus-Hermann
Markus-Hermann

Reputation: 987

Building Qt project with CMake and inheriting from QMainWindow leads to unreferenced vtable error

This is about a Qt 5.3.2 project buildt using CMake.

I have designed a QMainWindow using the Qt Designer, leading to main.ui.

CMakeLists.txt (the almost complete thing may be found here where I already posted it for a different question: Linking and UIC order in a CMake Qt project ) already takes care of calling UIC so I have my hands on ui_main.h.

ui_main.h offers the class Ui::MainWindow with the plain form information where all the buttons and stuff should be and the method *void setupUi(QMainWindow MainWindow).

Now my workflow (is it even a feasible one?) goes like this: I build a totally new header file Form_main.h:

// Form_main.h
[..]
class Form_main : public MainWindow, public QMainWindow
{
  Q_OBJECT
  privat slots:
    void on_some_event();
[..]
};

The class uses said auto-generated MainWindow::setupUi(this) to 'get in shape' and QMainWindow to be, well, a QMainWindow with all that stands for.

But now I am in a dilemma: Either I remove the Q_OBJECT macro call leading to connect(..) no longer recognizing that Form_main has signal slots, or I keep the Q_OBJECT leading to the infamous

undefined reference to `vtable for display::Form_main'

error while linking the project.

Now, there have been, in fact, people with similar issues. Naming some links:

http://michael-stengel.com/blog/?p=103

Qt Linker Error: "undefined reference to vtable"

Undefined reference to vtable... Q_OBJECT macro

Qt vtable error

A hint I got from the last one: "MOC must generate code for ui_main.h and the generated code must be compiled and linked."

In any case, these answers all seem to boil down to 'running qmake again'. Well, I use CMake all the way wanting my project to configure and compile after exactly

cmake .
make

What I did try was deleting everything in and below the build directory (including every auto-generated file) and then running cmake . && make;.

Sadly that did not help. I am afraid this is my second noob question today... would you bear with me once more?

=== AFTER TRYING GREENWAYS ANSWER I PROVIDE MORE DETAILS. ===

Here is the autogenerated ui_main.h

/********************************************************************************
** Form generated from reading UI file 'main.ui'
**
** Created by: Qt User Interface Compiler version 5.3.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_MAIN_H
#define UI_MAIN_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
[.. more Widget Includes ..]

QT_BEGIN_NAMESPACE

class Ui_MainWindow
{
    public:
        QAction *action_exit;
        [.. more sub widgets like that .. ]

    void setupUi(QMainWindow *MainWindow)
    {
      [ .. Setting up the form. Harmless code. .. ]
    } // setupUi

    void retranslateUi(QMainWindow *MainWindow)
    {
      [ .. completely harmless .. ]
    } // retranslateUi
};

namespace Ui {
    class MainWindow: public Ui_MainWindow {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_MAIN_H

Reading all this and incorporating your post right now I am at

// form_main.h

#ifndef MHK_FORM_MAIN_H
#define MHK_FORM_MAIN_H

#include <QMainWindow>
#include "ui_main.h"
[..]

namespace Ui { class MainWindow; }

namespace display
{
class Form_main : public QMainWindow
{
Q_OBJECT

private:
    ostream* stdout;
    ostream* stderr;

    Ui::MainWindow* uiMainWindow;

    /** Called by the constructor. Sets up event connections and other
     * preliminary stuff the qt Designer is overtasked with. */
    void setup_form();

[..]

public:
    explicit Form_main(QWidget* parent = 0);
    ~Form_main();

private slots:
    void exit_program();
};
}

#endif

And my cpp

// form_main.cpp

#include "ui_main.h"
#include "form_main.h"
[..]

using namespace Ui;

namespace display
{
void Form_main::setup_form()
{
    QObject::connect(uiMainWindow->action_exit, SIGNAL(triggered()), this, SLOT(exit_program()));
    [..]
}

Form_main::Form_main(QWidget* parent) : QMainWindow(parent)
{
    uiMainWindow = new Ui::MainWindow();
    uiMainWindow->setupUi(this);
    [..]
#if defined(Q_OS_SYMBIAN)
  this->showMaximized();
#else
  this->show();
#endif
}

Form_main::~Form_main()
{
    delete uiMainWindow;
}
[..]
Form_main::exit_program()
{
    this->close();
    (*stdout) << "Thanks for playing " << getProgramName() << endl;
}
}

Upvotes: 0

Views: 711

Answers (2)

Markus-Hermann
Markus-Hermann

Reputation: 987

Thanks for all your help! However, the problem was in CMakeLists.txt after all. The comment of Chris Morlier on Undefined reference to vtable pointed me to the solution.

The pertinent passage goes:

For Qt users: you can get this same error if you forget to moc a header

I simply had to add the header form_main.h into this line:

QT5_WRAP_CPP(qt_H_MOC ${qt_H} "${DIR_SRC}/include/form_main.h")

Upvotes: 1

Greenflow
Greenflow

Reputation: 3989

Ok. I see (partly) the problem. Just create a widget class like this:

.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

.cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

This is how the QtCreator creates ui-Widgets. "ui_MainWindow.h" is your generated .h file.

Upvotes: 1

Related Questions