[SOLVED] Access function in the mainwindow class from another class
-
wrote on 20 Sept 2014, 17:18 last edited by
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_OBJECTpublic:
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_OBJECTpublic:
explicit AddGameWizard(QWidget *parent = 0);
~AddGameWizard();private:
Ui::AddGameWizard *ui;
Settings settings;
void accept();
};#endif // ADDGAMEWIZARD_H
@Thanks for any help!
-
wrote on 20 Sept 2014, 18:11 last edited by
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?
-
wrote on 20 Sept 2014, 18:13 last edited by
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? -
wrote on 20 Sept 2014, 18:21 last edited by
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.
-
wrote on 20 Sept 2014, 18:23 last edited by
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();
}
@ -
wrote on 20 Sept 2014, 18:31 last edited by
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. :)
-
wrote on 20 Sept 2014, 18:34 last edited by
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!
-
wrote on 20 Sept 2014, 18:42 last edited by
No prob -- Enjoy!
-
wrote on 20 Sept 2014, 18:42 last edited by
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?
-
wrote on 20 Sept 2014, 19:15 last edited by
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_OBJECTpublic:
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.
-
wrote on 20 Sept 2014, 19:20 last edited by
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_OBJECTpublic:
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!
-
wrote on 20 Sept 2014, 19:25 last edited by
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_OBJECTpublic:
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.
-
wrote on 20 Sept 2014, 19:29 last edited by
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 -
wrote on 20 Sept 2014, 19:57 last edited by
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?
-
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
-
wrote on 20 Sept 2014, 21:48 last edited by
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_OBJECTpublic:
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
-
wrote on 20 Sept 2014, 22:15 last edited by
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.
1/17