[SOLVED] Access function in the mainwindow class from another class



  • Alright, so I have the following scenario.
    I have a main window which contains a treewidget with some log items. The mainwindow class has a public function called setStatus which adds an item to the log. My mainwindow class also has a variable which is my settings class. I want to use the setStatus function from within my settings class. I've thought about using the emit function to call the setStatus function from within the settings class, however another class, which is the class of another dialog (addgamewizard.cpp), also uses the settings class. So if I create the settings class from within that dialog's class, I won't be able to use emit because the dialog's class will be the parent of the settings class and not the mainwindow in which the setStatus function is.

    Okay, so basically this is that in code (it's simplified):
    mainwindow.cpp:
    @#include "mainwindow.h"
    #include "ui_mainwindow.h"

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

    QTreeWidgetItem* MainWindow::setStatus(const QString &message, const QString &type, QTreeWidgetItem *parent) {
    ui->label_Status->setText(message);
    QTreeWidgetItem *item = new QTreeWidgetItem();
    item->setText(0, QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz"));
    item->setText(1, type.toUpper());
    item->setText(2, message);
    if(parent == 0) {
    ui->treeWidget_Console->addTopLevelItem(item);
    } else {
    parent->addChild(item);
    }
    ui->treeWidget_Console->expandAll();
    return item;
    }

    void MainWindow::on_pushButton_Games_Library_AddGame_clicked() {
    AddGameWizard wizard(this);
    wizard.exec();
    }@

    mainwindow.h:
    @#ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    #include "addgamewizard.h"
    #include "settings.h"

    namespace Ui {
    class MainWindow;
    }

    class MainWindow : public QMainWindow {
    Q_OBJECT

    public:
    explicit MainWindow(QWidget parent = 0);
    ~MainWindow();
    QTreeWidgetItem
    setStatus(const QString &message, const QString &type, QTreeWidgetItem *parent = 0);

    private:
    Ui::MainWindow *ui;
    Settings settings;
    };

    #endif // MAINWINDOW_H
    @

    settings.cpp:
    @#include "settings.h"

    Settings::Settings() : QSettings(qApp->applicationDirPath() + "/settings.ini", QSettings::IniFormat) {
    }

    void Settings::addGame(const QString &gameName, const QString &gameExecutablePath, const bool &enableModManager, const QString &gameIconPath) {
    // THIS IS WHERE I WANT TO USE THE setStatus FUNCTION
    }@

    settings.h:
    @#ifndef BHSETTINGS_H
    #define BHSETTINGS_H

    #include "mainwindow.h"

    class Settings : public QSettings {
    public:
    Settings();
    void addGame(const QString &gameName, const QString &gameExecutablePath, const bool &enableModManager, const QString &gameIconPath = "");
    };

    #endif // BHSETTINGS_H
    @

    addgamewizard.cpp:
    @#include "addgamewizard.h"
    #include "ui_addgamewizard.h"

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

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

    void AddGameWizard::accept() {
    settings.addGame(ui->lineEdit_GameName->text(), ui->lineEdit_GameName->text(), ui->radioButton_ModsYes->isChecked(), ui->lineEdit_Icon->text());
    close();
    }@

    addgamewizard.h:
    @#ifndef ADDGAMEWIZARD_H
    #define ADDGAMEWIZARD_H

    #include "settings.h"

    namespace Ui {
    class AddGameWizard;
    }

    class AddGameWizard : public QWizard {
    Q_OBJECT

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

    private:
    Ui::AddGameWizard *ui;
    Settings settings;
    void accept();
    };

    #endif // ADDGAMEWIZARD_H
    @

    Thanks for any help!



  • Why can you not use a signal & slot? Or just pass it? If MainWindow is your top level widget, and it has the Settings class, getting it to anywhere should be somewhat trivial, no?



  • Well I'm still somewhat new to c++ so I'm not sure how I would get the MainWindow from within the addgamewizard class. Is there a function to get the main window? Because I would need to use the setStatus function as slot.
    So is there a function to get the top level widget / main window?



  • What does your main.cpp look like?

    Generally if you initialize both objects in the same scope, you can easily access either of them: this goes for getting and setting variables (assuming the methods/members are public), and of course, using QObject::connect() to do the signal() and slot() method.



  • My main.cpp is pretty much still left as it was when I created my project. So you're saying I should move all my initialization code from the constructor in my mainwindow class to the main class?

    This is my main.cpp:
    @#include "mainwindow.h"
    #include <QApplication>

    // TODO: Allow users to use arguments to start the program
    int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
    }
    @



  • Actually, I didn't need that. :)

    @void MainWindow::on_pushButton_Games_Library_AddGame_clicked() {
    AddGameWizard wizard(this);
    // wizard.setSettings( settings );
    wizard.exec()@

    See where I added a comment in the function above. You will need to add a function to your AddGameWizard class. It must go under public, but you could probably figure that out. It would look something like:

    void AddGameWizard::setSettings( Settings s ) { settings = s; }

    If you're going to be passing around your Settings file to lots of other objects, you should look into passing a pointer, for a few reasons. Not sure how new you are to C++, but they are a key part of the language. They of course, open up some headaches though. :)



  • Alright, thanks! Now I look at it, this seems to be the best way indeed :)

    And yeah I've been fiddling around with pointers for the last few days so I'm going to use those.

    Thanks for the help!



  • No prob -- Enjoy!



  • Actually, I'm getting a new error now.

    It's when the settings variable is set in the addgamewizard class.

    This is the error:
    C:\Qt\5.3\mingw482_32\include\QtCore\qsettings.h:192: error: 'QSettings::QSettings(const QSettings&)' is private
    Q_DISABLE_COPY(QSettings)
    ^

    It seems I'm still copying the variable instead of passing a pointer.

    This is my setSettings function:
    @void AddGameWizard::setSettings(Settings &arg1) {
    settings = arg1;
    }@

    And the settings variable in the header file:
    @private:
    Settings *settings;@

    This should work right? Or am I missing something?



  • The Q_DISABLE_COPY() part means you can't copy it around. I forgot how this is set in Qt (there are a number of ways to do this in C++, usually by making operator=() private. Qt probably uses some macro though ).

    Do something like this:
    in AddGameWizard you should have a place for a pointer to settings (Settings*)

    @class AddGameWizard : public QWizard {
    Q_OBJECT

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

    /* Right here. :) /
    void setSettings( Settings
    s ) { settings = s; }

    private:
    Ui::AddGameWizard *ui;

    /* This is now a pointer /
    Settings
    settings;
    void accept();
    };@

    Don't forget though it's best practice to make sure the default value of any member of a class which is a pointer to NULL. So, make sure your AddGameWizard constructor looks like this:

    AddGameWizard::AddGameWizard(QWidget *parent) : QWizard(parent), ui(new Ui::AddGameWizard), settings( NULL )
    {
    ui->setupUi(this);
    }

    If you don't, the default value of a pointer is garbage, and accessing it will lead to undefined behavior (most likely a coredump).

    Be warned here... any changes you make to settings within AddGameWizard are changed within MainWindow. Can't say for certain but this is usually the way a "settings" should work... at least that I can think of.

    Protip: within AddGameWizard, any time you access settings, check it for NULL.

    if ( !settings ) {
    // BOO... can't access it.
    }

    To get it out of your MainWindow, you need something like this:

    @Settings* MainWindow::getSettings() { return &settings; }@

    See how that works.



  • Alright, so that's working now, and again another new error. It seems I get a loop with includes. Because I need to use my mainwindow class from my settings class, however my mainwindow class also includes my settings class.

    The headers:
    mainwindow.h:
    @#ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    #include "settings.h"

    namespace Ui {
    class MainWindow;
    }

    class MainWindow : public QMainWindow {
    Q_OBJECT

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

    private:
    Ui::MainWindow *ui;
    Settings *settings;
    };

    #endif // MAINWINDOW_H
    @

    settings.h:
    @#ifndef SETTINGS_H
    #define SETTINGS_H

    #include "mainwindow.h"

    class Settings : public QSettings {
    public:
    Settings(MainWindow *arg1);
    void addGame(const QString &gameName, const QString &gameExecutablePath, const bool &enableModManager, const QString &gameIconPath = "");

    private:
    MainWindow *window;
    };

    #endif // SETTINGS_H
    @

    I think I need forward declarations but I have no idea how they work. I've looked it up and tried it but it failed to compile. Because I need actual functions from each class, I would essentially have to define all functions in both header files right? I think that would make everything a lot harder to change later on. Could you maybe give an example? And I really appreciate the help!



  • I replied (a rather long one) but I can't post for some reason.

    This can be frustrating and it killed me a few times.

    In any header file where you only have a pointer to an object, do not include it's header. Instead, simply declare it with:

    class ClassName;

    The source file (.cpp) requires the header included.

    However, I don't think your Settings class needs (or should have) a MainWindow* object. This is a design question and probably beyond the scope of this thread.

    Just in case, here's my reply that wouldn't go through...

    The Q_DISABLE_COPY() part means you can't copy it around. I forgot how this is set in Qt (there are a number of ways to do this in C++, usually by making operator=() private. Qt probably uses some macro though ).

    Do something like this:
    in AddGameWizard you should have a place for a pointer to settings (Settings*)

    @class AddGameWizard : public QWizard {
    Q_OBJECT

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

    /* Right here. :) /
    void setSettings( Settings
    s ) { settings = s; }

    private:
    Ui::AddGameWizard *ui;

    /* This is now a pointer /
    Settings
    settings;
    void accept();
    };@

    Don't forget though it's best practice to make sure the default value of any member of a class which is a pointer to NULL. So, make sure your AddGameWizard constructor looks like this:

    AddGameWizard::AddGameWizard(QWidget *parent) : QWizard(parent), ui(new Ui::AddGameWizard), settings( NULL )
    {
    ui->setupUi(this);
    }

    If you don't, the default value of a pointer is garbage, and accessing it will lead to undefined behavior (most likely a coredump).

    Be warned here... any changes you make to settings within AddGameWizard are changed within MainWindow. Can't say for certain but this is usually the way a "settings" should work... at least that I can think of.

    Protip: within AddGameWizard, any time you access settings, check it for NULL.

    if ( !settings ) {
    // BOO... can't access it.
    }

    To get it out of your MainWindow, you need something like this:

    @Settings* MainWindow::getSettings() { return &settings; }@

    See how that works.



  • Alright thanks I'll do that then. However, just for reference since I'm still learning, how would you recommend I access the setStatus function in my MainWindow class then? Because I would need the actual mainwindow class to access its functions right? Or is there another way?
    Sorry for all the questions :P



  • No problem on the questions... I ask a lot too, and I know it's frustrating.

    Generally the design I would do is that in your MainWindow, you have your Settings. It can be a pointer, just make sure you initialize it (with new Settings() ) before anyone accesses it. In the constructor could be okay. As such, MainWindow doesn't need a setStatus() function. Only other objects (which need the Settings) will have a SetStatus() function.

    From this point, I would access the pointer and pass it to any other object that needs it with the following public methods:

    @// Assuming it's not a pointer in MainWindow
    Settings* MainWindow::getSetings() { return &settings; }@

    @// Assuming it is a pointer
    Settings* MainWindow::getSettings() { return settings; }@

    Just about anything should be accessible from your MainWindow at some point. So when you create your AddGameWizard, which you're doing within a member function of MainWindow, do something like:

    @AddGameWizard wizard( this );
    myWizard.setSettings( this->getSettings() ); // "this->" isn't required, but easy to read.
    wizard.exec()@

    Your AddGameWizard should have a pointer to it. If your wizard makes a change to the Settings, your main window will know.

    Make sense?


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    For your last include problem, it's called circular dependencies and the only solution is indeed forward declaration.

    However, if your Settings object inherits from QSettings, then there's no need to pass it around. QSettings allows to access settings of your application anywhere. You just create a QSettings object, use it and done.

    You should really take the time of going through Qt's tutorials, examples and demos. Get a feeling of signals and slots and how to avoid unnecessary dependencies



  • EDIT: Never mind, fixed it! I added #include "mainwindow.h" to my settings.cpp class and now it works.

    Alright I've read a lot of posts and searched a lot but I can't get this to work and it is really frustrating. Basically what I'm trying to do is have the main class use functions from the settings class and vice versa. I can't use emit since I need to get a return value and emit signals happen asynchronously.

    Here is all the relevant code (I've removed irrelevant functions and includes):
    mainwindow.h:
    @#ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    #include "settings.h"

    namespace Ui {
    class MainWindow;
    }

    class MainWindow : public QMainWindow {
    Q_OBJECT

    public:
    explicit MainWindow(QWidget parent = 0);
    ~MainWindow();
    QTreeWidgetItem
    setStatus(const QString &message, const QString &type, QTreeWidgetItem *parent = 0);

    private:
    Ui::MainWindow *ui;
    Settings settings;
    };

    #endif // MAINWINDOW_H@

    mainwindow.cpp:
    @#include "mainwindow.h"
    #include "ui_mainwindow.h"

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

    QTreeWidgetItem* status;

    // Actually load the settings
    status = setStatus("Loading settings", "Status");
    QHash<int, QHash<QString, QVariant> > games = settings.getGames();
    for(int i = 0; i < games.size(); i++) {
    QTreeWidgetItem *item = new QTreeWidgetItem;
    item->setText(0, games.value(i).value("Name").toString());
    ui->treeWidget_Games_Library->addTopLevelItem(item);
    }
    ui->frame_Console->setVisible(settings.getConsoleOpen());
    ui->pushButton_Console->setVisible(settings.getShowConsole());
    if(settings.getStartMaximized()) {
    setWindowState(Qt::WindowMaximized);
    }
    Theme::loadTheme(settings.getTheme());
    setStatus("Done loading settings", "Status", status);

    // Make the widgets reflect the settings
    status = setStatus("Initializing interface", "Status");
    ui->comboBox_Theme->setCurrentIndex(ui->comboBox_Theme->findText(settings.getTheme()));
    ui->checkBox_ShowConsole->setChecked(settings.getShowConsole());
    ui->checkBox_ShowExperimental->setChecked(settings.getShowExperimental());
    ui->checkBox_StartMaximized->setChecked(settings.getStartMaximized());
    setStatus("Done initializing interface", "Status", status);
    }

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

    QTreeWidgetItem* MainWindow::setStatus(const QString &message, const QString &type, QTreeWidgetItem *parent) {
    ui->label_Status->setText(message);
    QTreeWidgetItem *item = new QTreeWidgetItem();
    item->setText(0, QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz"));
    item->setText(1, type.toUpper());
    item->setText(2, message);
    if(parent == 0) {
    ui->treeWidget_Console->addTopLevelItem(item);
    } else {
    parent->addChild(item);
    }
    ui->treeWidget_Console->expandAll();
    return item;
    }@

    settings.h:
    @#ifndef SETTINGS_H
    #define SETTINGS_H

    #include "mainwindow.h"

    class Settings : public QSettings {
    public:
    Settings(MainWindow arg1);
    void setDefaultValues();
    void addGame(const QString &gameName, const QString &gameExecutablePath, const bool &enableModManager, const QString &gameIconPath = "");
    QHash<int, QHash<QString, QVariant> > getGames();
    bool getConsoleOpen();
    void setConsoleOpen(const bool &open);
    bool getShowConsole();
    void setShowConsole(const bool &showConsole);
    bool getShowExperimental();
    void setShowExperimental(const bool &showExperimental);
    bool getStartMaximized();
    void setStartMaximized(const bool &startMaximized);
    QString getTheme();
    void setTheme(const QString &theme);

    private:
    MainWindow window;
    };

    #endif // SETTINGS_H
    @

    settings.cpp:
    @#include "settings.h"

    Settings::Settings(MainWindow arg1) : QSettings(qApp->applicationDirPath() + "/settings.ini", QSettings::IniFormat), window(arg1) {
    }

    void Settings::setDefaultValues() {
    // This function just sets the default settings
    }

    void Settings::addGame(const QString &gameName, const QString &gameExecutablePath, const bool &enableModManager, const QString &gameIconPath) {
    QTreeWidgetItem* status = window->setStatus("Adding game "" + gameName + """, "Status");
    setDefaultValues();
    int size = value("Games/size").toInt();
    beginWriteArray("Games");
    setArrayIndex(size);
    setValue("Name", gameName);
    setValue("ExecutablePath", gameExecutablePath);
    setValue("IconPath", gameIconPath);
    setValue("EnableModManager", enableModManager);
    endArray();
    window->setStatus("Game added", "Status", status);
    }

    QHash<int, QHash<QString, QVariant> > Settings::getGames() {
    QHash<int, QHash<QString, QVariant> > result;
    int size = beginReadArray("Games");
    for (int i = 0; i < size; ++i) {
    setArrayIndex(i);
    result[i]["Name"] = value("Name");
    result[i]["ExecutablePath"] = value("ExecutablePath");
    result[i]["IconPath"] = value("IconPath");
    result[i]["EnableModManager"] = value("EnableModManager");
    }
    endArray();
    return result;
    }

    bool Settings::getConsoleOpen() {
    setDefaultValues();
    return value("State/ConsoleOpen").toBool();
    }

    void Settings::setConsoleOpen(const bool &open) {
    setValue("State/ConsoleOpen", open);
    }

    // The other functions are like the two above
    @

    And this doesn't work. I know why it doesn't work, because each header includes the other so you get an infinite loop. I have no idea how I would apply forward declarations to this, since I've never used them before and when I try to do it I get a lot of errors about incomplete classes.

    If anyone could please provide an example of how to do what I'm trying to do relevant to the code posted above I would be very, very grateful.

    Thanks in advance



  • Okay, so you're all sorted now?

    EDIT: Ah, marked solved. Would seem so.
    Welcome to C++ & Qt.

    While it's easier to learn Qt after C++, I did both at the same time (more or less). I had a brief head start on C++, so it's definitely doable.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.