Escrevendo sua navegação no Qt

Olá pessoal. Sou um desenvolvedor Android com pouca experiência. E hoje gostaria de compartilhar a experiência de desenvolver um projeto educacional em C ++ e Qt com navegação simples entre telas. Eu ficaria feliz em ouvir críticas ou acréscimos à minha solução e espero que ela possa ajudar alguém e tornar a vida mais fácil.





Tarefa

Antes de começar, vamos definir dois termos para que no final não haja confusão.

Window é a janela mais comum no Windows ou em qualquer outro sistema operacional.

Tela - algum tipo de conteúdo que pode ser substituído por outros dentro da janela.





, - Trello, , . ? , .





, , QStackedWidget . - .





. , , . .





, , , :





  1. .





  2. .





  3. .





  4. .





BaseFragment. , . , .





class BaseFragment: public QFrame {
    Q_OBJECT

signals:
    //      
 
public:
    BaseFragment();
    ~BaseFragment();

    //  -   
};
      
      



BaseFragment. , QStackedWidget .





class LoginFragment: public BaseFragment

class StartFragment: public BaseFragment

class RegistrationFragment : public BaseFragment
      
      



, , , . , , .





class BaseScreensFactory {
public:
    BaseScreensFactory();
    ~BaseScreensFactory();

    virtual BaseFragment* create(QString tag);
    virtual QString createStart();
};

      
      



:





  1. create(QString tag) — ( );





  2. createStart() — .





:





// screensfacrory.h  
namespace screens {
    static const QString SPLASH_TAG = "splash";
    static const QString START_TAG = "start";
    static const QString LOGIN_TAG = "login";
    static const QString REGISTRATION_TAG = "registration";
  	//   .....
};

class ScreensFactory: public BaseScreensFactory {
public:
    ScreensFactory();
    ~ScreensFactory();

    BaseFragment* create(QString tag) override;
    QString createStart() override;
};

// screensfacrory.cpp 
BaseFragment* ScreensFactory::create(QString tag) {
    qDebug("ScreensFactory create");
    if (tag == SPLASH_TAG) {
        return new SplashFragment;
    } else if (tag == START_TAG) {
        return new StartFragment;
    } else if (tag == LOGIN_TAG) {
        return new LoginFragment;
    } else if (tag == REGISTRATION_TAG) {
       //   .....
    }
}

QString ScreensFactory::createStart() {
    return SPLASH_TAG; //   .
}
      
      



, , , . QStackedWidget .





:





  • navigateTo(tag) — .





  • back() — .





  • replace(tag) — .





  • newRootScreen(tag) — .





  • navigateToWhithData(tag, data) — , navigateTo(tag), - .





, , , navigateTo(REGISTRATION_TAG), , . newRootScreen(MAIN_TAG).





, , BaseFragment , .





class BaseFragment: public QFrame {
    Q_OBJECT

signals:
  	// 
    void back();
    void navigateTo(QString tag);
    void newRootScreen(QString tag);
    void replace(QString tag);
    
    void navigateWhithData(QString tag, BaseModel* model);
 
public:
    BaseFragment();
    ~BaseFragment();

  	//    
    virtual void onPause();
    virtual void onResume();
    virtual void setData(BaseModel* model);
};
      
      



, , , onPause(), , onResume() , . setData() .





. QStackedWidget BaseScreensFactory .





navigator.h:





class Navigator: public QObject {
    Q_OBJECT
private:
    QStackedWidget *currentContainer;
    BaseScreensFactory *screensFactory;
    QLinkedList<BaseFragment*> stack;

    /**
     * @brief createAndConnect
     * @param tag   .
     *
     *      
     *   .
     *
     * @return     .
     */
    BaseFragment* createAndConnect(QString tag);

    /**
     * @brief connectFragment
     * @param fragment   
     *          .
     *
     *   
     *     
     *     .
     *
     */
    void connectFragment(BaseFragment *fragment);

    /**
     * @brief disconnectFragment
     * @param fragment
     *
     *    .
     */
    void disconnectFragment(BaseFragment *fragment);
public:
    Navigator(
            QStackedWidget *container,
            BaseScreensFactory *screensFactory
    );
    ~Navigator();
    BaseFragment* getStartScreen();

public slots:
    /**
     * @brief navigateTo
     * @param tag   .
     *
     *    .
     */
    void navigateTo(QString tag);

    /**
     * @brief back
     *
     *    .
     */
    void back();

    /**
     * @brief replace
     * @param tag    
     *         .
     *
     *     
     *  .
     */
    void replace(QString tag);

    /**
     * @brief newRootScreen
     * @param tag    
     *         .
     *
     *       
     *   .
     */
    void newRootScreen(QString tag);

    /**
     * @brief navigateWhithData
     * @param model
     *
     *   navigateTo   .
     */
    void navigateWhithData(QString tag, BaseModel* model);
};
      
      



. . . connectFragment , . disconnectFragment . createAndConnect . getStartScreen , , .





BaseFragment* Navigator::getStartScreen() {
    return createAndConnect(this->screensFactory->createStart());
}

void Navigator::connectFragment(BaseFragment *fragment) {
    connect(fragment, &BaseFragment::back, this, &Navigator::back);
    connect(fragment, &BaseFragment::replace, this, &Navigator::replace);
    connect(fragment, &BaseFragment::navigateTo, this, &Navigator::navigateTo);
    connect(fragment, &BaseFragment::newRootScreen, this, &Navigator::newRootScreen);
    connect(fragment, &BaseFragment::navigateWhithData, this, &Navigator::navigateWhithData);
}

void Navigator::disconnectFragment(BaseFragment *fragment) {
    disconnect(fragment, &BaseFragment::back, this, &Navigator::back);
    disconnect(fragment, &BaseFragment::replace, this, &Navigator::replace);
    disconnect(fragment, &BaseFragment::navigateTo, this, &Navigator::navigateTo);
    disconnect(fragment, &BaseFragment::newRootScreen, this, &Navigator::newRootScreen);
    disconnect(fragment, &BaseFragment::navigateWhithData, this, &Navigator::navigateWhithData);
}

BaseFragment* Navigator::createAndConnect(QString tag) {
    BaseFragment *fragment = this->screensFactory->create(tag);
    connectFragment(fragment);
    return fragment;
}
      
      



, .





Navigator::Navigator(
        QStackedWidget *container,
        BaseScreensFactory *screensFactory
) {
    this->screensFactory = screensFactory;
    this->currentContainer = container;
    BaseFragment* startFragment = getStartScreen();
    this->stack.append(startFragment);

    currentContainer->addWidget(stack.last());
    currentContainer->setCurrentIndex(0);
}
      
      



. : navigateTo. , . .





void Navigator::navigateTo(QString tag) {
    BaseFragment *newFragment = this->screensFactory->create(tag);
    stack.last()->onPause();
    disconnectFragment(stack.last());
    connectFragment(newFragment);
    stack.append(newFragment);
    currentContainer->addWidget(newFragment);
    currentContainer->setCurrentWidget(newFragment);
}
      
      



, . , . .





MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{   
    try {
        container = new QStackedWidget;

        this->factory = new ScreensFactory;
        this->navigator = new Navigator(
                    this->container,
                    this->factory
        );

        this->setCentralWidget(container);
    } catch (std::exception& e) {
        qDebug("%s", e.what());
    }
}
      
      



Agora que vimos um grande número de trechos de código, a princípio pode parecer que eles não estão muito conectados. Depois de muito tentar descobrir isso, esse sentimento não desaparece, e isso é realmente uma coisa boa. Como resultado, pudemos desenvolver várias telas do aplicativo de forma totalmente independente do resto das partes. Escrevemos a implementação BaseFragment e a adicionamos à fábrica.





Minha solução me ajudou muito na implantação do projeto. Consegui acelerar o desenvolvimento e dividir o código.





Obrigado a todos que leram, espero que isso ajude alguém.





Aqui está o link GitHub do projeto








All Articles