I'm selected for the STM32H7B3I-DK - DISCOVERY KIT road test.
I'm working on a touch screen GUI for my electronic load.
I'm going to review the ModelListener today. A software component that takes care that your business logic (electronic lab instrument in my case) does not need to know what screen is active, and what it displays.
video: capture in the simulator. This is no longer a mock GUI but a real GUI with a mock SCPI instrument.
I've reviewed the Model-View-Presenter framework before.
I focused there on using RTOS queues to exchange data and commands between GUI and backend. And how the main objects in the MVP work together.
The goal of that post was to show abstraction between backend and GUI. Backend is an electronic load that knows how to act as an electronic load.
Front end is specialised in displaying things and taking user input. It does not know anything about constant current, ADCs, DACs, electronics...
The only GUI class and backend class that talk together are the SCPI client (representing the instrument) and the TouchGFX Model class (representing the gateway with the backend).
Image: Model does not know each screen but knows the backend. Backend knows nothing about the GUI at all. It receives and sends messages.
A rich GUI can have a number of screens. And we want to prevent that the TouchGFX business level gateway, the Model, has to know each screen, or what each screen needs.
That's why they introduced the ModelListener.
I'll talk about the c++ part soon. Let's first discuss the goal.
A Model gets messages from the backend. The backend informs the Model about the measured current, measured voltage, temperature, ...
The Model will want to pass that info on to the screen. But not all screens want the same info. And not all screens want it in the same form.
We want to avoid that the Model needs to know what the screen wants, and what it doesn't need. No logic in the Model based on the active screen, if at all possible.
Now c++:
The ModelListener is a base class for each screen that gladly takes any message from the Model, and does nothing with it.
It silently takes the message and returns control back to the Model. Not interested.
class ModelListener { public: virtual void scpi_idn(const char* data) {} virtual void scpi_function(const char* data) {}and };
If you have a screen that is not interested when the Model receives the instrument's SCPI ID, it will gladly be ignored. The virtual function is empty and the message dies silently.
c++ optimisers make this efficient. Not a lot overhead is burned if your screen isn't interested.
But each TouchGFX Presenter class inherits from ModelListener. There's a Presenter class generated (and associated) for each screen you design. It's the backbone of the screen.
That means that you can choose if you ignore a message, or act on it, in the Presenter class of your screen.
If you aren't interested in changing the active screen when a SCPI ID arrives, you do nothing in the Presenter for that screen.
If you want to do something - my start screen wants to display the ID as part of the init sequence - you implement the listener method.
image: one Model, one ModelListener. Each screen has a tied (tight) pair of Presenter and View
You tell the compiler you're fair game in the header file:
class MainPresenter : public Presenter, public ModelListener { public: void scpi_idn(const char* data); };
... and pick up the signal in the implementation of that class:
void MainPresenter::scpi_idn(const char* data) { view.status(data); }
In my implementation, the Presenter (tightly coupled to the graphic screen - represented by a View class in TouchGFX) knows that when a ID arrives, the screen want to update it's status.
If needed, the presenter can massage the data so that it's in a good format for the View. In our case, no massaging is needed.
We just send the data to the View. The View will update the right GUI widget(s) and redraw what needs to be redrawn.
void MainView::status(const char* data) { Unicode::strncpy(txtStatusBuffer, data, TXTSTATUS_SIZE); txtStatus.invalidate(); }
In essence, the whole organisation is a decision engine without IFs and CASEs. As a screen, you tell the compiler you want to receive a notification by implementing it.
For the c++ fanboys (like me).
Top Comments