user742925
user742925

Reputation: 285

QT/C++ - Accessing MainWindow UI from a different class

I'm a beginner to both C++ and Qt, so perhaps this is trivial. It certainly feels like it should be simple, but I've been searching for an answer for a few hours now and can't find the solution. I'm making a simple board game where the MainWindow's ui (made in QtDesigner) contains a canvas for the game board (a QGraphicsView). Now, the main.cpp is as simple as can be:

MainWindow Game;

int main(int argc, char *argv[])
{
 QApplication a(argc, argv);

 Game.show();

return a.exec();
}

Since I need to access and edit the MainWindow Widgets from another totally unrelated class, I thought the easiest way would be to just make MainWindow a global variable. It seems that this approach was very wrong, though. Upon trying to run the project in QtDesigner I get a Microsoft Visual C++ runtime library error: the application has requested runtime to terminate it in an unusual way.

So what is the correct way to do what I need?

Aside from the MainWindow I have a dialog for a new game (QDialog, generated from QtDesigner) that is displayed after clicking a menu item in MainWindow. When the user inputs all parameters for the game and clicks OK in the dialog, I instantiate a custom non-Qt class called GameState. This class is meant to operate the game itself, draw the board, prompt the user, etc. However, as this class is created in the QDialog, it does not know of the existence of a MainWindow and so I cannot do anything with the MainWindow from this class. How can I modify the MainWindow from an unrelated class, then?

Also, jsut how does the setEnabled() function work? It never seems to do anything. Any widget I set as disabled in the QtDesigner and then try to enable through this function still stays disabled in the GUI...

Upvotes: 23

Views: 62255

Answers (6)

francek
francek

Reputation: 512

In the past I used approach described in this answer (found in Qtractor project).

Now I use QObject 'name' property and discover it anywhere as described here.

main.c

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.cpp

#include <QString>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "c.h"

MainWindow * MainWindow::pMainWindow = nullptr;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    pMainWindow = this;
    setCentralWidget(&m_pb);
    connect(&m_pb, SIGNAL(clicked()), this, SLOT(on_pb_clicked()));
}

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

// kind of singleton reference.
MainWindow *MainWindow::getMainWinPtr()
{
    return pMainWindow;
}

void MainWindow::pbSetText()
{
    m_pb.setText(QString{"Call from c."});
}

void MainWindow::on_pb_clicked()
{
    c C;  // call of MainWindow from class c ctor
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QString>
#include <QPushButton>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    static MainWindow * getMainWinPtr();
    void pbSetText();

public slots:
    void on_pb_clicked();

private:
    static MainWindow * pMainWindow;

    Ui::MainWindow *ui;
    QPushButton m_pb{QString{"Press me."}, this};
};

#endif // MAINWINDOW_H

c.cpp

#include "c.h"
#include "mainwindow.h"

c::c()
{
    MainWindow * mw = MainWindow::getMainWinPtr();
    mw->pbSetText();
}

c.h

#ifndef C_H
#define C_H

class c
{
public:
    explicit c();
};

#endif // C_H

Upvotes: 2

Flot2011
Flot2011

Reputation: 4671

I am doing it this way:

QMainWindow* getMainWindow()
{
    foreach (QWidget *w, qApp->topLevelWidgets())
        if (QMainWindow* mainWin = qobject_cast<QMainWindow*>(w))
            return mainWin;
    return nullptr;
}

Upvotes: 11

Tom
Tom

Reputation: 71

The easiest way to do this is to first set up a signal in the header file of your other class to say perform a function to manipulate an object in the main class like this

signals:
    void disableLoadButtton();

Then create a slot under private slots in the header file of the main window like this

private slots:
     void disableLoadButtton();

Then create the function as a members function in the main window to manipulate the object

void MainWindow::disableLoadButton()
{
     ui->loadButton->setenabled(false);
}

Then add the following line in another member function of the main window which say sets up the page. My other class is called searchWidget

void MainWindow::setUpPage()
{
    connect(searchWidget, SIGNAL(disableLoadButton()), this, SLOT(disableLoadButton()));
}

Then all you have to do to disable the loadButton (which is a object in MainWindow) is to add the following line in any members function of my other class searchWidget

void searchWidget::performSomething()
{
      emit disableLoadButton();
}

This will then manipulate the object loadButton in the mainwindow from within a member function of the other class searchWidget.

Upvotes: 7

Dariusz Scharsig
Dariusz Scharsig

Reputation: 1220

First off it's a bad idea to create MainGame before you create your QApplication object. If you want to have your MainGame object globally available like this it should be a pointer:

MainWindow *Game;
int main (int argc, char **argv)
{
  QApplication a (argc, argv);

  Game = new MainWindow();
  Game->show();

  int result = a.exec();

  delete Game;
  Game = NULL;

  return result;
}

This approach is however not the most elegant. There are two much better choices.

  1. The QApplication object actually stores all top level windows like your MainGame which means you can allways aquire it through QApplication::topLevelWidgets() which is a static function and returns a list with all top level widgets. Since you only have one, the first one is your MainGame. The drawback is you'll have to cast it, but using Qts qobject_cast<MainGame*>(...) is fairly safe. You'll have to check the result though to make sure it isn't a NULL pointer.

  2. Use the singelton design pattern. You should store the global Game pointer in the source (cpp) file of the Game class itself (subclass QMainWindow) and your Game class should implement a static public method which returns this global pointer. So if any other class needs the Game pointer, it simply calls:

    MyGame *theGame = MyGame::getInstance();
    

    for example.

Regarding your setEnabled() problem. Please post the relevant code. If it's too much feel free to send me the *.ui file and the piece of code via mail.

Best regards
D

Upvotes: 17

Ignacius
Ignacius

Reputation: 59

If you have to access your MainWindow from another window, you are probably doing it wrong. Using another class to pass information with signals/slots is probably a much better approach

Upvotes: 1

Alex Reche Martinez
Alex Reche Martinez

Reputation: 966

If your application has only one window you can simply use:

MainWindow * win = (MainWindow *) qApp::activeWindow();

Upvotes: 8

Related Questions