Solved How to properly manage a Multi-Page application (without QStackedWidget)
-
Hi Everyone,
I'm trying to create a UI with lot of pages. I know that QStackedWidget Class can do the job but because the target is a raspberry pi I don't want to load all pages (i.e. QFrame) in the memory. Thus I'm trying to create/delete each Pages to replace the QMainWindow::centralWidget. Note that each Pages is a Class which inherits from QFrame. That works.But, I want to include a [BACK] button and so I need to save the previous Page constructor to recall it when this button is pressed. I'm trying to use function pointer pointing on a static "factory" method defined in each Page Class.
Page1::Page1(QWidget *parent) : QFrame (parent){} Page1* Page1::factory() { return new Page1();}
Because each page is an instance of a different class i can't do this :
Page1* (*previousPage) (void); previousPage = &Page1::factory;
What I'm trying to do is to use the centralWidget to pointing to their factory method.
Maybe is not the good strategy to managed multiPage including a [back] button.
Can you help me please.
ThanksFoufy
-
@Foufy
Before you go any further with this strategy. When user goes Back do you expect them to get back to the previous page with all state (e.g. widgets) just as it was, or do you expect them to always have to restart from a brand new page in its initial state? because you're going to get the latter, and you may say you actually want the former -
This post is deleted! -
Hi @JonB, I want to create a totally new page.
-
@Foufy
I am not a C++ expert. So I cannot be sure my answer is correct, but I do not think you can achieve what you want via your simple approach.C++ templates may help you. Have a look at
https://gist.github.com/sacko87/3359911
https://stackoverflow.com/questions/4357500/c-abstract-factory-using-templates
http://blog.fourthwoods.com/2011/06/04/factory-design-pattern-in-c/ (looks scary to me)
https://www.codeproject.com/Articles/363338/Factory-Pattern-in-Cplusplus looks fairly simple to me (no templates), but you would have to read through the comments, because I think some people are saying it is not good design.At the end of the day they (mostly?) use a pattern of the factory linking a string name (via
Register
) for the desired class with a constructor for that class ( viaCreate
).Depending on just how many classes of pages you have to deal with, you could cut out all the complications and just go for explicit code with a
switch
/if else if
directly calling the necessarynew
s and then it's done without arguing....One other way would be a dynamically-populated
QStackedWidget
with the pages, but not pre-created with all page types, rather only those in the currently Backable navigation. Remove pages from the stack once they cannot be Backed into. This would be more like when I asked whether you want to preserve previous state. It will take up more space than no-saved-stack, though not as much as all-pre-created, which you may say is unacceptable for your RPi memory, I don't know. -
@JonB
thanks for your point of view. I'll read this quickly but also trying to understand it ;) -
What about if each of Page's class emits a Signal such :
#include "Page2.h" Class Page1: public QFrame { public: static QFrame* factory(); signals: void frameChanged(QFrame* (*previousFrame)(void), QFrame* nextFrame); }
and when a new page is created in Page1.cpp
void Page1::onNextPageClicked() { emit frameChanged(&Page1::factory, new Page2); }
-
@Foufy
That, and whether you do or do not emit signals, has no connection to your question about how/whether to create a factory for the various page classes you have. -
This post is deleted! -
@Foufy
I've test what i suggested but can't compile (Qt5.12.1). It seems not possible to pass a function pointer by signal/slot. I have the following error-
./build-TestMultiPage-Desktop_Qt_5_12_1_GCC_64bit-Debug/moc_mainwindow.cpp:78: erreur : no matching function for call to ‘MainWindow::onCenterFrameChanged(QFrame*&, QFrame*&)’
case 1: _t->onCenterFrameChanged((reinterpret_cast< QFrame()>(_a[1])),(reinterpret_cast< QFrame()>(_a[2]))); break; -
list item../TestMultiPage/mainwindow.h:24: candidate: void MainWindow::onCenterFrameChanged(QFrame* ()(), QFrame)
void onCenterFrameChanged(QFrame*(previousFrame)(void),QFrame nextFrame);
^~~~~~~~~~~~~~~~~~~
-
-
I dont know if it works in your case, but have you tried using a QList / QVector or something to store the name or the object in a List (FILO, first in last out)?
When the "Back"-Btn is pressed, you detroy / replace the active widget, call the constructor of the last widget from your QLIst and set it as CentralWidget...@Foufy said in How to properly manage a Multi-Page application (without QStackedWidget):
I've test what i suggested but can't compile (Qt5.12.1). It seems not possible to pass a function pointer by signal/slot. I have the following error
Are you using the new signal&slot syntax or the old one?
With the new one, you can pass nearly everything (compiler will check, if it is possible).From https://wiki.qt.io/New_Signal_Slot_Syntax:
-
Pros
- Compile time check of the existence of the signals and slot, of the types, or if the Q_OBJECT is missing.
- Argument can be by typedefs or with different namespace specifier, and it works.
- Possibility to automatically cast the types if there is implicit conversion (e.g. from QString to QVariant)
- It is possible to connect to any member function of QObject, not only slots.
-
-
I use the old syntax. But the problem is at the slot declaration level.
This do not compilepublic slots: void onCenterFrameChanged(QFrame*(*previousFrame)(void),QFrame* nextFrame);
But compilation is OK if I declare a method (instead of a slot) like this
public: void onCenterFrameChanged(QFrame*(*previousFrame)(void),QFrame* nextFrame);
-
I finally found a solution
#include <QFrame> class AbstractPage : public QFrame { Q_OBJECT public: explicit AbstractPage(QWidget *parent = nullptr){}; virtual AbstractPage* (*getFactory())() = 0; };
An exemple of a Page
#include "abstractframe.h" class Page1 : public AbstractFrame { Q_OBJECT public: explicit Page1(QWidget *parent = nullptr); inline static AbstractFrame* factory(){return new Page1;}; inline AbstractFrame* (*getFactory())() {return &Page1::factory;}; private slots: void onButtonClicked(bool); signals: void frameChanged(AbstractFrame*); };
And generate a new Page using
AbstractFrame *(*previousFrame)() = mCenterFrame->getFactory(); // and after create the Frame mCenterFrame = previousFrame();