2024-08-05 01:16 PM
I can see from logging that my Presenter's #activate is called after the screen's #setupScreen.
This seems wrong. The screen depends on the presenter. There should be an opportunity for the presenter to initialize itself before the screen uses it.
Am I misunderstanding something?
Thanks.
Solved! Go to Solution.
2024-08-07 04:26 AM
The activate() function is used to start using the screen's presenter and screen's view because TouchGFX shows only one screen at a time. When the presenter is set to be active, then the activate() function is called. By "Initialization logic" we mean the steps that are required for showing the screen.
For instance, imagine that you have a screen that shows a Wi-Fi icon (it is shown in an image widget) when the Wi-Fi is accessible, and hides it if it is not available. When this screen is going to be shown, in other words, it is switched in, TouchGFX knows that this screen is made out of a number of widgets plus our Wi-Fi icon image. These widgets are part of the view. Now that we know the elements, we activate the presenter, and from the presenter we can ask the state of the Wi-Fi from the model. Therefore, we can call a method of the view to either show the Wi-Fi icon or not from the presenter based on the status received from the model. Hence, the presenter needs to know what functions or widgets are available in the view, and that's why the setupScreen() is called first.
I hope this clears out the obfuscation.
You can check out more documentation about the concept of screen here, and there is an example called Understanding Application Structure available in TouchGFX that can showcase the connection between Model-View-Presenter:
2024-08-06 07:38 AM - edited 2024-08-06 07:39 AM
Hello @farble1670,
That's a fair and good question.
If you look at the generated code for your ScreenPresenter.hpp, you will see that the constructor takes a reference to the corresponding screen as an argument. Hence, it means the presenter depends on the screen.
#ifndef SCREEN1PRESENTER_HPP
#define SCREEN1PRESENTER_HPP
#include <gui/model/ModelListener.hpp>
#include <mvp/Presenter.hpp>
using namespace touchgfx;
class Screen1View;
class Screen1Presenter : public touchgfx::Presenter, public ModelListener
{
public:
Screen1Presenter(Screen1View& v);
/**
* The activate function is called automatically when this screen is "switched in"
* (ie. made active). Initialization logic can be placed here.
*/
virtual void activate();
/**
* The deactivate function is called automatically when this screen is "switched out"
* (ie. made inactive). Teardown functionality can be placed here.
*/
virtual void deactivate();
virtual ~Screen1Presenter() {}
private:
Screen1Presenter();
Screen1View& view;
};
#endif // SCREEN1PRESENTER_HPP
You can read more about the Model-View architecture used in TouchGFX here. .
I hope this answers your question. Don't hesitate to ask more!
2024-08-06 09:24 AM - edited 2024-08-06 09:25 AM
@Mohammad MORADI ESFAHANIASL wrote:
If you look at the generated code for your ScreenPresenter.hpp, you will see that the constructor takes a reference to the corresponding screen as an argument. Hence, it means the presenter depends on the screen.
Let's get back to the root problem. If the view can't rely on the presenter being "activated" before it's used, what's the point of having an activate method? Here's your generated code:
/**
* The activate function is called automatically when this screen is "switched in"
* (ie. made active). Initialization logic can be placed here.
*/
virtual void activate();
It says it right there: "Initialization logic can be placed here".
I guess we could debate what's meant by "screen is switched in" but it certainly implies before the system starts calling methods in the (view) object.
2024-08-07 04:26 AM
The activate() function is used to start using the screen's presenter and screen's view because TouchGFX shows only one screen at a time. When the presenter is set to be active, then the activate() function is called. By "Initialization logic" we mean the steps that are required for showing the screen.
For instance, imagine that you have a screen that shows a Wi-Fi icon (it is shown in an image widget) when the Wi-Fi is accessible, and hides it if it is not available. When this screen is going to be shown, in other words, it is switched in, TouchGFX knows that this screen is made out of a number of widgets plus our Wi-Fi icon image. These widgets are part of the view. Now that we know the elements, we activate the presenter, and from the presenter we can ask the state of the Wi-Fi from the model. Therefore, we can call a method of the view to either show the Wi-Fi icon or not from the presenter based on the status received from the model. Hence, the presenter needs to know what functions or widgets are available in the view, and that's why the setupScreen() is called first.
I hope this clears out the obfuscation.
You can check out more documentation about the concept of screen here, and there is an example called Understanding Application Structure available in TouchGFX that can showcase the connection between Model-View-Presenter:
2024-08-07 09:18 AM
Okay, maybe I'm getting it. This is what I do now:
What you're suggesting, I think:
Basically, the flow of data is from presenter -> view, only. The view never pulls data from the presenter. User input / actions flow from the view to the presenter, which may result in the presenter calling back into the view with updated data values.
Note that there's no "event" in my situation. The data is just there in the model, and it's put there prior to the screen being active. So I think you're saying I need to create an "artificial" event in ::activate.
2024-08-08 03:11 AM
@farble1670 wrote:What you're suggesting, I think:
- in ::setupScreen, we don't access the presenter. I guess all we are supposed to do here is position things, set colors, and other static configuration for the view. We don't access any data.
- in Presenter::activate, we call into the view with the string (e.g. MyView::updateString(...).
- The screen sets the string into the text area.
This part is correct. But, keep in mind that presenter::activate() is not the only function that you can use to send data to the view. You can define custom functions to read data from on demand from the presenter, or even send data to the presenter. Imagine you have a button in your GUI that when is clicked, you want turn on a fan or a motor. In this case, the state of the button should be reported to the presenter and presenter conveys it to the model, and model can write to the corresponding output pin.
@farble1670 wrote:Note that there's no "event" in my situation. The data is just there in the model, and it's put there prior to the screen being active. So I think you're saying I need to create an "artificial" event in ::activate.
You don't require any specific event, as you mentioned, you just need to get the data from the model and feed it to your view in the presenter. Something like below:
Screen1Presenter.cpp
#include <gui/screen1_screen/Screen1View.hpp>
#include <gui/screen1_screen/Screen1Presenter.hpp>
Screen1Presenter::Screen1Presenter(Screen1View& v)
: view(v)
{}
void Screen1Presenter::activate()
{
int newFanSpeed = model->getFanSpeed(); //Or any data that you have
view.updateTheFanSpeed(newFanSpeed);
}
void Screen1Presenter::deactivate()
{}
Screen1View.cpp
#include <gui/screen1_screen/Screen1View.hpp>
Screen1View::Screen1View()
{}
void Screen1View::setupScreen()
{
Screen1ViewBase::setupScreen();
}
void Screen1View::tearDownScreen()
{
Screen1ViewBase::tearDownScreen();
}
void Screen1View::updateTheFanSpeed(int newFanSpeed)
{
Unicode::snprintf(textArea1Buffer, TEXTAREA1_SIZE, "%d", newFanSpeed);
textArea1.invalidateContent();
}