Ecrire votre navigation dans Qt

Bonjour à tous. Je suis un développeur Android avec peu d'expérience. Et aujourd'hui, je voudrais partager l'expérience de développement d'un projet éducatif en C ++ et Qt avec une navigation simple entre les écrans. Je serais heureux d'entendre des critiques ou des ajouts à ma solution et j'espère que cela pourra aider quelqu'un et me faciliter la vie.





Tâche

Avant de commencer, définissons deux termes pour qu'il n'y ait finalement pas de confusion.

La fenêtre est la fenêtre la plus courante dans Windows ou tout autre système d'exploitation.

Écran - une sorte de contenu qui peut être remplacé par d'autres à l'intérieur de la fenêtre.





, - 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());
    }
}
      
      



Maintenant que nous avons examiné un grand nombre de morceaux de code, au début, il peut sembler qu'ils ne sont pas très connectés. Après avoir beaucoup essayé de le comprendre, ce sentiment ne disparaît pas, et c'est en fait une bonne chose. En conséquence, nous avons pu développer différents écrans de l'application de manière totalement indépendante du reste des pièces. Nous écrivons l'implémentation BaseFragment et l'ajoutons à l'usine.





Ma solution m'a beaucoup aidé dans la mise en œuvre du projet. J'ai pu accélérer le développement et diviser le code.





Merci à tous ceux qui ont lu, j'espère que cela aidera quelqu'un.





Voici le lien GitHub du projet








All Articles