Как получить доступ из вложенного объекта к главному

97
29 ноября 2021, 03:40

У меня есть главное окно приложения BankSystem

#include "session.h"
#include "registration.h"
#include "login.h"
class BankSystem : public QMainWindow
{
    Q_OBJECT
public:
    BankSystem(QWidget *parent = nullptr);
    ~BankSystem();
    Login* getLogin();
private:
    Ui::BankSystem *ui;
    Login *login;
    Registration *registration;
    Session* _currentSession;
};

Есть указатель на класс Login. При запуске приложения первым запускается окно входа, т.е. Login и, если авторизация прошла успешна, я должен засетать текущую сессию. У меня стоит слот, который отлавливает событие нажатия на кнопку Войти. И тут возникает проблема, как из этого слота засетать текущую сессию? Или как изменить логику, чтобы такая операция стала возможной?

#include "banksystem.h"
BankSystem::BankSystem(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::BankSystem)
{
    ui->setupUi(this);
    login = new Login();
}

Класс Login

class Login : public QWidget
{
    Q_OBJECT
public:
    explicit Login(QWidget *parent = nullptr);
    ~Login();
private slots:
    void on_loginButton_clicked();
    void on_registrationButton_clicked();
private:
    Ui::Login *ui;
};
#include "login.h"
void Login::on_loginButton_clicked()
{
    if (ui->loginSection->text().isEmpty()
            || ui->passwordSection->text().isEmpty())
    {
        QMessageBox msgBox;
        msgBox.setText("Необходимо ввести логин и пароль!");
        msgBox.setIcon(QMessageBox::Icon::Information);
        msgBox.exec();
        return;
    }
    QString login = ui->loginSection->text();
    QString password = ui->passwordSection->text();
    try {
        // Вот тут мне нужно засетать сессию
    } catch (std::exception& e) {
        qDebug() << e.what();
    }
}
Answer 1

1) Создаем в классе Login сигнал loginOk

signals:
    void loginOk();

2) Высылаем сигнал при успехе в Login::on_loginButton_clicked():

void Login::on_loginButton_clicked()
{
...
   emit loginOk();
...
}

3) Создаем слот BankSystem::onLoginOk():

public slots:
    void onLoginOk();

4) В котором и "сетим" сессию:

void BankSystem::onLoginOk()
{
...
    session->set(true); //не знаю, как у вас
    //опционально удаляем сендер с помощью deleteLater()
...
}

5) Соединяем сигнал и слот:

connect(login, &Login::loginOk, bankSystem, &BankSystem::onLoginOk);

6) Радуемся работе программы

Такой подход наиболее каноничен, православен и кошерен с точки зрения QT. Соединив объекты один раз, больше не надо заботится об их взаимодействии и взаимных вызовах, каждый объект будет жить своей автономной жизнью, а взаимодействие будет происходить автоматически. К тому же, не требуется использовать указатель на Login в качестве члена BankSystem. Таким же способом можно соединить Registration и Login.

Замечу, хоть и не относится к вопросу: Если требуется более-менее сложная логика логинга, сессий и регистрации, я бы выделил отдельные классы для логинга, сессии, креденциалов и регистрации и для соответсвующих окон логинга и регистрации, вместо вкладывать логику в виджетные классы. Для простой проверки в стиле if (password == "iloveu") - сойдет

Answer 2

Вам класс логин нужен на один раз, так зачем его вообще создавать через указатель? Лучше создавать и вызывать в модальном режиме в локальной памяти, для этого надо использовать QEventLoop:

class Login ... {
public:
    Login() {
        isLogged = false;
        connect(ui->buttonOk, Login::yes, [=]() {
            isLogged = true;
            _loop.quit();
        });
    }
    bool exec() {
        _loop.exec();
        return _isLogged;
    }
    QEventLoop _loop;
    bool _isLogged;
}

Теперь в main.cpp:

... main() {
    ...
    Login login;
    bool isLogged = login.exec();
    // Или, если надо дать пользователю шанс исправить ввод:
    while(isLogged = login.exec()) {
        login.setErrorMessage("Попробуйте еще раз!");
    }
    // Можно ограничить количество попыток и так далее
    // Кроме того, этот while лучше перенести внутрь exec() и 
    // там уже реализовать необходимую логику, 
    // но это вопрос вашей архитектуры
    if( _isLooged ) {
        BankSystem w;
        w.show();
        return a.exec();
    }
    return 0;
}

Теперь видно, что сессию, например, можно стартонуть прямо из конструктора главного окна приложения:

class BankSystem : ... {
    ...
public:
    BankSystem() {
        _session->start();
    }
}

А можно, если нужно проверять состояние сессии, сделать отдельный метод:

class BankSystem {
    void sessionStart() {
        if( !_session->start() ) {
            // Сессия не запустилась, надо что-то делать 
        }
    }
}

Я рекомендую вам сделать класс сессии синглтоном, так как обычно сессией управляет один объект, а сверять и изменять ее состояние вероятно понадобится из разных функций и методов, разной степени вложенности:

class Session {
public:
    static Session& instance() {
        return self == null? self = new Session(): self;
    }
private:
    Session* self;  
}

Теперь в любом месте программы, в любом методе:

Session::instance().<какая-то функция>

И не надо будет думать о бесконечных вызовах и коннектов

READ ALSO
cpp:template - Как решить проблему &quot; cannot convert from &#39;char* const&#39; to int &quot;?

cpp:template - Как решить проблему " cannot convert from 'char* const' to int "?

Разбираюсь с template-ми на c++Написал небольшую функцию которая принимает, какой-то объект MyJSON* _value и в реализации сделал разбор в зависимости...

140
Compile time C++ checksum

Compile time C++ checksum

Есть структура:

222
Проблема с отображением текстур

Проблема с отображением текстур

Здраствуйте, пробую реализовать арканоид, но столкнулся с проблемой, что текстуры привязуется к не правильным участкам пространстваУ меня...

260
Ошибка при переписывание кода в функции

Ошибка при переписывание кода в функции

Попалась вот такая задача:

129