Indirect QMainWindow subclasses and .ui files
-
I am using Qt Creator 5.4. I am creating an application which uses several different QMainWindow subclasses. Because the subclasses share some core functionality, I would like to derive from QMainWindow, put the core functionality in that subclass, and then derive the various actual main windows from that subclass. So, for the sake of illustration, let's say:
class BaseWindow : public QMainWindow;
class MainWindow : public BaseWindow // 1st sub-subclass
class AltWindow : public BaseWindow // 2nd sub-subclassAdditionally, because there is substantial similarity between the MainWindow and AltWindow UIs, I would like to be able to copy, say, the MainWindow .ui file and import it into AltWindow, and then make some changes to the latter via Qt Designer.
The solution I have tried results in a proper display of widgets in MainWindow and AltWindow classes, but the signals/slots are not working. Here is what I tried.
The initial problem is that Add new... | Qt | Qt Designer Form | Choose a form template will only let me choose a template based on QMainWindow, rather than my BaseWindow class above. So, to get around this, I created BaseWindow using the Designer Form Class template. Then, I went into the project file directory, copied BaseWindow.ui and renamed the copy "MainWindow.ui". I added that file to the project using "Add Existing Files..." I added a button widget to the .ui file using the Qt Designer. In Designer, I connected the PushButton::clicked() signal to a corresponding slot in MainWindow.\
I also figured it was necessary to open the MainWindow.ui file using the plain text editor, to change "BaseWindow" to "MainWindow" in the following xml snippet:
<class>MainWindow</class> <widget class="MainWindow" name="MainWindow">
I then created a MainWindow class, derived from BaseWindow, as a c++ class rather than a Qt Designer Form template. I manually added the code necessary to create the UI object (I am using the "single inheritance" approach described in the Qt documentation), called ui->setup(this), etc. The relevant code:
#include "MainWindow.h" #include "ui_MainWindow.h" MainWindow::MainWindow(QWidget *parent) : BaseWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } // Etc.
I also added the necessary code to mainWindow.h:
namespace Ui { class MainWindow; } class MainWindow : public BaseWindow { . . . private slots: void on_mainBtn_clicked(); private: Ui::MainWindow *ui; };
As I noted,MainWindow displays properly, but the QPushButton::clicked() signal does not work. Notably, the "connections" tag in MainWindow.ui is empty (I assume that's where they go when sig/slots are created through Qt Designer?) I also tried manually adding a sig/slot connection in the MainWindow constructor:
QObject::connect( ui->mainBtn, SIGNAL(clicked()), this, SLOT(on_mainBtn_clicked() ) );
but Qt Creator application output pane gives the following error message:
Debugging starts QObject::connect: No such slot BaseWindow::on_mainBtn_clicked() in ..\UiFiletest2\MainWindow.cpp:9 QObject::connect: (sender name: 'mainBtn') QObject::connect: (receiver name: 'MainWindow') Debugging has finished
Note the reference to BaseWindow rather than MainWindow, even though both the connection code and the slot code are definitely in MainWindow.
Where am I going wrong? Is there a better way to create QMainWindow sub-subclasses that derive from a child of QMainWindow? Any suggestions would be much appreciated.
-
Hello,
It's pretty unusual to have multiple main windows, but should be possible in principle. Do you have theQ_OBJECT
macro in all of your derived classes? -
Aha! Yes, that worked. I am an idiot. Thank you!
As to your other observation, I am creating, in effect, a real-time strategy game. The opening window is a plain widget containing a QStackedLayout. The top widget in the stack is a selection page, with several buttons that allow the user to go to the map editor, scenario editor, single player game, network game, etc. Each of these sub pages is its own MainWindow subclass.
I had been meaning to ask whether there is a better way to accomplish this common game architecture. Can you suggest anything? My initial thought was to simply have a single MainWindow object that dynamically reconfigures its UI depending on the specific "page" (e.g., map editor, single-player game, etc.) specified by the user. But that seemed like a very complicated way to do things.
-
As to your other observation, I am creating, in effect, a real-time strategy game.
Curiously enough I'm doing the same, although I have decided to give Qt3D a try for that purpose.
The opening window is a plain widget containing a QStackedLayout. The top widget in the stack is a selection page, with several buttons that allow the user to go to the map editor, scenario editor, single player game, network game, etc. Each of these sub pages is its own MainWindow subclass.
I'd suggest doing that the other way around. Firstly your tools (scenario/map editor) I'd put in their separate application, not in the main one. This is usually the case for most games, as they are not essential to play the game. This has the side effect that you don't create/manage widgets for things you won't be needing, thus reducing memory and CPU requirements. Secondly, you'd make a generic widget, having the ability to add some "toolbox" widgets, as the central/main widget of your main window. I don't know whether it's a good idea to stack main windows, you might be in for some trouble with that. If you still want the stack widget, you can use it as a central widget and add your
QWidget
(derived) pages to it. The menu actions and the like can be connected to the appropriate slots at runtime, so I don't see much point of stackingQMainWindow
s.I hope that helps.
Kind regards.