24

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...

1

6 Answers 6

17

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

Sign up to request clarification or add additional context in comments.

Comments

11

I am doing it this way:

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

1 Comment

For Qt 5+ use QGuiApplication::topLevelWindows() which returns a QList<QWindow*>.
8

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

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

9 Comments

Don't use C-style cast in C++. Use qobject_cast or static_cast instead.
static_cast<MainWindow*>(parent())->theMethodYouWantToCall();
Also, this approach will not work, if MainWindow doesn't have the keyboard focus = if it is not active.
For reference, now In Qt5, this is QApplication::activeWindow().
This approach fails in too many cases so it should not be used. If the main window does not have keyboard focus (e.g. after being brough up by clicking on task bar), then qApp::activeWindow() returns nullptr.
|
7

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.

1 Comment

How to create searchWidget object?
2

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

Comments

1

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

2 Comments

If we need to the observer design pattern, we need to pass the view(MainWindow) to controller class so cotoller can access its' method.
Ah, yes. The inevitable "You're doing it wrong!" non-answer. There are perfectly valid reasons to directly access a singleton main window. Parental finger wagging is rarely a helpful response. This is no exception. We're all responsible adults here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.