Escribiendo su navegación en Qt

Hola a todos. Soy un desarrollador de Android con poca experiencia. Y hoy me gustaría compartir la experiencia de desarrollar un proyecto educativo en C ++ y Qt con simple navegación entre pantallas. Me encantaría escuchar críticas o adiciones a mi solución y espero que pueda ayudar a alguien y hacer la vida más fácil.





Tarea

Antes de comenzar, definamos dos términos para que al final no haya confusión.

Window es la ventana más común en Windows o en cualquier otro sistema operativo.

Pantalla: algún tipo de contenido que puede ser reemplazado por otros dentro de la ventana.





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



Ahora hemos mirado una gran cantidad de piezas de código, al principio puede parecer que no están muy conectadas. Después de mucho intentar averiguarlo, este sentimiento no desaparece, y en realidad es algo bueno. Como resultado, pudimos desarrollar varias pantallas de la aplicación de forma completamente independiente del resto de partes. Escribimos la implementación de BaseFragment y la agregamos a la fábrica.





Mi solución me ayudó mucho en la implementación del proyecto. Pude acelerar el desarrollo y dividir el código.





Gracias a todos los que han leído, espero que esto ayude a alguien.





Aquí está el enlace de GitHub del proyecto.








All Articles