cancel
Showing results for 
Search instead for 
Did you mean: 

TouchGFX kept in memory my previous settings for DigitalClock (removed widget) [solved]

ELero.1
Associate III

Hello,

I created a test project (Riverdi_101STM32H7-4.20) on the side of my main project the other day about managing a clock between multiple screens via a container (see here : https://community.st.com/s/question/0D53W00001g13KBSAY/custom-container-on-multiple-screen-set-digitalclock-problem-solved )

I recently tried to replace my digitalClocks in my screens of my main project by a digitalClock in a container (banner). I copied exactly the same code as in my test project but it failed to work. Indeed, the behavior of the clock was really weird. It started back at 08:25:36 in the first screen (normal because it's the time I set), but then it was 00:00:00 in the second screen and 12:40:28 in the third one.

I tried everything, renaming mytickCounter, renaming the clockbanner widget, but nothing worked.

I printed on my screens the tickCounter/60 to see the problem.

  • 1st screen: number of seconds/ticks goes well. It stops counting (pauses) when I change screens but comes back to the good number when I I get back to the 1st screen.
  • 2d and 3d screen the tick goes back to 0 (it didn't go back to 0 in my test project).

Now, I saw that when I waited 3 seconds in my 1st screen and came back to my 3d, the digitalClock of the 3d screen had passed 3 hours and not 3 seconds. And there was some other weird behavior of time adding up when I changed screens.

Now ... I remember exactly setting in my DigitalClock of the 3d screen (so before I changed it for a banner/container) the time 12:40:28 in the Designer (and 00:00:00 in the 2d screen). But I deleted ALL the digitalClocks of my project in the designer to only leave the one in my banner (which is set to the typical 10:10:00 and not 12:40:28). They had the same name though.

My guess is that the designer or the project kept in memory the information for the previous digitalClocks and didn't erase everything when I deleted the widgets from my screens.

So I tested something out. I recreated from scratch my project, added the banner, copied the GUI entire file (so all my personal code) and now it works perfectly fine, like my test project.

  1. How to be sure that the designer or the project doesn't keep in memory the previous widgets even if they have the same name as the old deleted widgets please?
  2. I've already started from scratch and wouldn't want to do it again if it happened again in the future, could you see if there is a problem with TouchGFX or if I did something wrong, please?
  3. Do you know if there is a way to keep the screens and code of my project but removing all the temporary files (wouldn't want to start from scratch again ^^)

I cannot share my current (failing) project a it has personal info in it, that's why I created a test project in the beginning, to be able to share that one (shared in the link at the beginning of my question).

Thanks a lot in advance,

Eve

14 REPLIES 14
Yoann KLEIN
ST Employee

Hello @ELero.1​ ,

After discussing those weird issues with my colleagues, we found out that your project needs a complete functioning change.

To help you change your project, I made an example for you, with 3 screens and a clock + counter running simultaneously on the 3 screens, without any bugs or reset of your values.

You will find the project attached to this post.

Of course, I will now explain you what was wrong and what we have done :

1) In your project, the handleTickHandler() method is called in the Banner directly (and then linked to a view). Since you want to share same clock values with all 3 screens, this method should cycle in a common place between this 3 screens : the Model. So, in Model.cpp, you have to include your clock routine inside the tick() method.

void Model::tick()
{
    tickCounter++;
    
    if (tickCounter % 60 == 0)
    {
        secondsCounter++;
        modelListener->updateCounterValue(secondsCounter);
 
        if (++seconds >= 60)
        {
            seconds = 0;
            if (++minutes >= 60)
            {
                minutes = 0;
                if (++hours >= 24)
                {
                    hours = 0;
                } // End hours
            } // End minutes
        } // End seconds
 
        updateClock();
 
    } // End modulo
}

Then, you need to declare the updateClock() method, also in the Model.cpp class. This method will provide the updated values to the modelListener.

void Model::updateClock()
{
    // Update the clocks
    modelListener->updateClockValues(hours,minutes,seconds);
    modelListener->updateCounterValue(secondsCounter);
}

2) After that, you will have to declare 2 virtual functions in your ModelListener.hpp class, in order to propagate later the value to the Presenters (because the ScreenPresenter.hpp of your screens inherit from the ModelListener).

    virtual void updateClockValues(int hour, int minute, int second){
 
    }
 
    virtual void updateCounterValue(int secondsCounter){
 
    }

3) Then, in each of your ScreenPresenter.hpp, you have to re-declare those virtual functions. That will enable you to get the clock and counter values into the Presenter class, and to give them later to the Views.

    virtual void updateClockValues(int hour, int minute, int second);
    virtual void updateCounterValue(int secondsCounter);

4) In your ScreenView.hpp files, add a function which will return the CustomContainer object of your screen. That's mandatory because you need to access the container's widgets from the presenter.

    ClockContainer& getClockContainer() {
        return clockContainer1;
    }

5) Then, in your Presenter.cpp, implement the 2 previous methods, in order to call the CustomContainer methods for updating the UI with clock data.

void Screen1Presenter::updateClockValues(int hour, int minute, int second){
    view.getClockContainer().setTime24Hour(hour, minute, second);
}
 
void Screen1Presenter::updateCounterValue(int secondsCounter){
    view.getClockContainer().updateCounter(secondsCounter);
}

6) Don't forget to declare the corresponding methods in the CustomContainer class.

void ClockContainer::setTime24Hour(uint8_t hour, uint8_t minute, uint8_t second)
{
    digitalClock1.setTime24Hour(hour,minute,second);
    digitalClock1.invalidate();
}
 
void ClockContainer::updateCounter(int secondsCounter)
{
    Unicode::snprintf(textArea1Buffer, TEXTAREA1_SIZE, "%d", secondsCounter);
    textArea1.invalidate();
}

That's the best, cleanest, and more efficient way to achieve what you want.

Don't hesitate to ask me if you have question or things you didn't succeed to implement/ didn't understand :)

/Yoann

Yoann KLEIN
ST Software Developer | TouchGFX

Hello @Yoann KLEIN​ ,

Thanks a lot for the help, it works in my bigger project too now. It's just a little bulky since I have to add the code in all my screens (I have A LOT of screens with the banner). But if that's the best way to do it, so be it ^^

I have two questions:

  • I don't understand why we pass through the ModelListener (Presenter of the screens inherit from the ModelListener) because we could previously obtain the time thanks to a function like this in the presenter.hpp of the screens, without passing by the listener. What's the added value of adding this step? The getTime function returns the clockvalues from the model.
MyClock getTimePres()
{
        return model->getTime();
}

  • Why create a function to get the container "getContainer" in ScreenView.hpp, and not just include the container file in the header of the presenter of the screen <gui/containers/Banner.hpp> ?

Thanks in advance,

Eve

Hello @ELero.1​ ,

  • Sorry I think I forgot something. Indeed, you can also use this method from the Presenter. Actually, in ScreenPresenter.cpp you can call the updateClock() method from the model in your activate() method. This will enable you to refresh your clock instantly every time you switch back to Screen1. Of course, you will have to do the same for every screens.
void Screen1Presenter::activate()
{
    model->updateClock();
}
  • The banner object is protected inside the ScreenViewBase class. That means that you will have to inherit from this class in your presenter to be able to call it directly. The ScreenView already inherits from ScreenViewBase, that's why it's more simple to do it that way.

/Yoann

Yoann KLEIN
ST Software Developer | TouchGFX

Hello @Yoann KLEIN​,

Indeed I noticed that the clock waited a second to update so I searched your code yesterday and found the bit of code you added in your message.

Though I still don't understand the purpose of passing through ModelListener compared to the simple function we had previously in the model. When we call UpdateClockValues from the model to the ModelListener, does it automatically call the UpdateClockValues from each screen presenter, therefore refreshing the time every second ? Is this done so we don't have to do a tick counter in our screens to go search for the time in the model every second ?

Thanks in advance,

Eve

Hello @ELero.1​ ,

That's exactly the point of using the ModelListener here.

Indeed, here you will implement the tick method inside your model class, and after every tick, you will call ModelListener functions. These functions are inherited in every of the 3 screen's Presenter, and then they update simultaneously screens with new values.

So, the point of using the ModelListener is that you get values from the same class (model) for all 3 screen, and that avoids a lot of issues, and it simplifies the code.

Hope that it helped,

/Yoann

Yoann KLEIN
ST Software Developer | TouchGFX