PDA

View Full Version : Friend Classes in Separate .h files



newBie123
18th June 2017, 21:13
Hello everyone, first of all I just start learning Qt.

I have MainWindow class defined in "mainwindow.h" and "mainwindow.cpp" files.
The "Dialog" and "Dialog_2" classes are friend classes of that MainWindow class.
An object of "MainWindow" class is parent of "Dialog" and "Dialog_2" type objects.
But I cannot access the members of MainWindow from both of that friend classes.
From the function "void Dialog_2::add_new_person()", I am trying to access the data_map member of parent object, but I cannot do that.

The code is:

"mainwindow.h"



#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
friend class Dialog;
friend class Dialog_2;
Q_OBJECT
.......
}


"mainwindow.cpp"


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dialog.h"
#include "dialog_2.h"

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


"dialog_2.h"


#ifndef DIALOG_2_H
#define DIALOG_2_H

#include <QDialog>

class MainWindow;

namespace Ui {
class Dialog_2;
}

class Dialog_2 : public QDialog
{
Q_OBJECT

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

private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();

private:
Ui::Dialog_2 *ui;
void add_new_person();
};

#endif // DIALOG_2_H


"dialog_2.cpp"


#include "dialog_2.h"
#include "ui_dialog_2.h"
#include <QMessageBox>
#include <QDebug>
#include "mainwindow.h"

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

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

void Dialog_2::on_pushButton_clicked()
{
add_new_person();
close();
}

void Dialog_2::on_pushButton_2_clicked()
{
close();
}

void Dialog_2::add_new_person()
{
QString new_name = ui->lineEdit_1->text();
QString new_number = ui->lineEdit_2->text();

this->parent()->data_map.insert(new_name, new_number);

QMessageBox::information(this, "Added!", new_name + " has added successfully!");
}


It's same for dialog.h class, so I didnot add the code.

Thanks for your help :)

d_stranz
19th June 2017, 17:14
The "Dialog" and "Dialog_2" classes are friend classes of that MainWindow class.

Wrong, wrong, wrong. Except in rare circumstance you never need friend classes, and certainly not for the case you are describing.

Your case is exactly what Qt's signals and slots are designed for. You want to add a new person to MainWindow's data structure from your Dialog class? Then add a signal to Dialog_2 to let MainWindow know:



class Dialog_2 /...
{
Q_OBJECT

// ...

signals:
void addNewPerson( const QString & name, const QString & number );

// ...
};



In your Dialog_2::add_new_person method, you do this:



void Dialog_2::add_new_person()
{
QString new_name = ui->lineEdit_1->text();
QString new_number = ui->lineEdit_2->text();

emit addNewPerson(new_name, new_number);
}

In your MainWindow class, you create a slot:



// MainWindow.h

class MainWindow : //...
{
Q_OBJECT
// ...

private slots:
void onAddNewPerson( const QString & name, const QString & number );

};

// MainWindow.cpp:

void MainWindow::onAddNewPerson( const QString & name, const QString & number )
{
data_map.insert( name, number );
}


And at the place where you invoke the Dialog_2 instance:



void MainWindow::getNewPersonInfo()
{
Dialog_2 dlg( this );
connect ( &dlg, &Dialog_2::addNewPerson, this, &MainWindow::onAddNewPerson );
dlg.exec();
}


And as soon as the Add button is clicked in Dialog_2, the signal gets sent to MainWindow, and the new person gets added. If you want to get feedback to Dialog_2 to report success, then you add a signal to MainWindow ("void newPersonAdded( bool bStatus )" ) that you emit after the insert into the map. If the insert succeeded, you pass "true" in the signal, otherwise you pass "false" as the "bStatus" value.

In Dialog_2, you add a slot (void onNewPersonAdded( bool bStatus ) ). In that slot you can display your QMessageBox with the "Success!" or "Failed!" message.

You add a second connection between the MainWindow's signal and the Dialog_2 slot at the same place you made the first connection:



void MainWindow::getNewPersonInfo()
{
Dialog_2 dlg( this );
connect ( &dlg, &Dialog_2::addNewPerson, this, &MainWindow::onAddNewPerson );
connect( this, &MainWindow::onNewPersonAdded, &dlg, &Dialog_2::onNewPersonAdded );
dlg.exec();
}

// and update this method:
void MainWindow::onAddNewPerson( const QString & name, const QString & number )
{
if ( data_map.contains( name ) )
emit newPersonAdded( false ); // duplicate name, so don't add it
else
{
data_map.insert( name, number );
emit newPersoanAdded( true );
}
}


No need for friend classes, no need for Dialog_2 to know how MainWindow stores person information, no need for Dialog_2 to even know there is a MainWindow class. The signals and slots take care of transmitting the information between Dialog_2 and MainWindow.

newBie123
19th June 2017, 19:46
Thank you so much for your detailed explanation. It helped me so much to understand signals and slots mechanism.
I think I need to refresh my knowledge in fundamentals before starting to write my project. :)