0

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;
}
}
Community
  • 1
  • 1
Markus-Hermann
  • 789
  • 11
  • 24
  • QMainWindow and MainWindow inherit from QObject... not possible. Only one QObject inheritance is allowed. – Greenflow Apr 30 '15 at 16:43
  • Just remove public QMainWindow – Greenflow Apr 30 '15 at 16:45
  • @Greenflow is right, you want to have `class Form_main : public MainWindow {`. – Simon Warta Apr 30 '15 at 16:49
  • @Simon and Greenflow: Thanks for your answers! Yet they raise another problem: setupUi(QMainWindow *MainWindow) requires a MainWindow instance (without the autogenerated MainWindow class being a QMainWindow itself). So Form_main can no longer say "this->setupUi(this)" if I do not inherit like stated in my question. – Markus-Hermann Apr 30 '15 at 16:56
  • Maybe I could build a class inheriting QObject as you suggest and having a basic QMainWindow as a member. Then I could use this member to have the window and the Q_OBJECT to have the slots. Would have prefered my approach... but if it doesn't work it doesn't do. – Markus-Hermann Apr 30 '15 at 17:00
  • Try: setupUi(this). MainWindow **is** a QMainWindow. – Greenflow Apr 30 '15 at 17:00
  • Unless of course you did something stupid like selecting QWindget in designer and call this MainWindow... but then you are out of luck and you can start over. – Greenflow Apr 30 '15 at 17:02
  • Sorry, but (just checked it in the designer) main.ui represents a genuine QMainWindow. And the autogenerated class does not inherit anything. Citing ui_main.h: "class Ui_MainWindow" and later "namespace Ui { class MainWindow: public Ui_MainWindow {}; }". Thats all. No QObject inheritance here. – Markus-Hermann Apr 30 '15 at 17:28

2 Answers2

1

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.

Greenflow
  • 3,935
  • 2
  • 17
  • 28
  • Thanks for your answer. I already tested it down to the forward declaration and the explicit constructor. However, it still does not solve my issue. What also bugs me is this in my opinion wacky MainWindow definition in the autogenerated file. It is just empty... please wait, I will post it. – Markus-Hermann Apr 30 '15 at 18:11
  • Your CMakeLists.txt looks strange. Don't know if it is wrong... more than one way to skin a cat. You use cmake 2.8.11. No need for **set(qt_H "${DIR_BUILD}/ui_main.h" "${DIR_BUILD}/ui_dialog_setup_game.h") # Generated using the trusty QtDesigner. set(qt_UI "${DIR_SRC}/ui/main.ui" "${DIR_SRC}/ui/dialog_setup_game.ui")** and **QT5_WRAP_UI(qt_UI_H ${qt_UI})**. I just add all .ui to add_executable and it works. – Greenflow Apr 30 '15 at 18:29
  • Nope, now with your edits... the generated .h looks good. CMakeLists.txt is fine. – Greenflow Apr 30 '15 at 18:34
  • I got it! Will post the answer. – Markus-Hermann Apr 30 '15 at 18:43
1

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")
Community
  • 1
  • 1
Markus-Hermann
  • 789
  • 11
  • 24
  • Interesting. I don't use QT5_WRAP_CPP anymore and it works.What version is your cmake? – Greenflow Apr 30 '15 at 18:49
  • Just in case you are interested... here is one of my CMakeList.txt files: http://pastebin.com/AnWAuTtj – Greenflow Apr 30 '15 at 18:55
  • I will take a look! As to your question: My CMakeLists.txt says cmake_minimum_required(VERSION 2.8.11). And cmake --version returns cmake version 3.0.2 – Markus-Hermann Apr 30 '15 at 19:02
  • Hmmm... yours is lacking the QT5_WRAP_CPP, QT5_WRAP_UI and QT5_ADD_RESOURCES all of which are crucial to mine own file. Maybe it is a system thingy. I work on Debian Linux and am hammering with vim, the console and KDevelop. cmake is cool. But I already learned that platform independence is a high goal. – Markus-Hermann Apr 30 '15 at 19:05
  • cmake version 3.1.20141224-gce73c. Also Debian Linux (unstable). I compiled my cmake myself from git dev branch. The Debian version had/has a bug... don't know which one anymore. – Greenflow Apr 30 '15 at 19:09
  • @Greenflow is using AUTOMOC. See http://www.cmake.org/cmake/help/v3.0/manual/cmake-qt.7.html – steveire Apr 30 '15 at 19:12
  • He does, too. But I think in his version it is still broken. – Greenflow Apr 30 '15 at 19:15
  • Actually I don't think my cmake is broken. But maybe I am! ;-) The form_main.h in my sick conception of C++ is no source file and its only reference within CMakeLists.txt is by its directory name within the include_directories(..) command. Hence I believe that AUTOMOC cannot find it. And it is this header file that contains the notorious Q_OBJECT. – Markus-Hermann Apr 30 '15 at 19:18
  • project(sentinel) set( CMAKE_AUTOMOC ON ) <---- in your CMakeLists.txt And I don't know why anymore **blush**... some weeks ago, I had to get a dev version of cmake. Something did not work. Must be a newer feature, because all my old stuff had no problems. High probability that this had something to do with AUTOMOC. – Greenflow Apr 30 '15 at 19:19
  • Yep. As I said: I believe AUTOMOC runs okay. However, it does not find form_main.h because I never register it neither in add_executable nor in add_library. Just a hunch. – Markus-Hermann Apr 30 '15 at 19:21
  • If AUTOMOC runs ok, you would not need QT5_WRAP_CPP, QT5_WRAP_UI and QT5_ADD_RESOURCES.---- Ok, never registering your generated .h file cannot work. This is the only disadvantage of cmake against qmake: Adding new files is perfectly solved in QtCreator. – Greenflow Apr 30 '15 at 19:23
  • That would depend on whether or not AUTOMOC scans the include_directories(..) for any single header it may find there. Since this may be a potentially very large number and generate tons of unnecessary code. I do not know if that would be a good idea. Still there would be no other way because the string "form_main.h" simply did not occur within my CMakeLists.txt file until half an hour ago. -- ah! Sorry. Answered your comment before I read it whole. – Markus-Hermann Apr 30 '15 at 19:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/76676/discussion-between-markus-hermann-and-greenflow). – Markus-Hermann Apr 30 '15 at 21:33