320 views
in GUI Development by

Hello,

I would like to close/discard a page based on the value of a variable.

Specifically, in my application I have parameter setup pages and a "work cycle" page that is displayed based on a start command, which can come from various sources (digital input, serial/LAN command, display button, etc.). Upon the start command, the interface sets variables on the device to execute the requested work cycle, execute the WorkCycleStart function in the device integration, and displays the CyclePage.

The machine is state-based, so when the work cycle is esecuted, the device status changes from "ready" to "running" (different values ​​of the DeviceStatus property). The change of the status is managed in the device integration code, which also updates the property value on the GUI (as explained in your DeviceIntegration examples). 

When the work cycle ends/interrupts, to exit the work cycle page, I have associated an observer to the state property in this page. When the variable changes and is no longer "Working" (value 5), the DismissDialog command is executed. 

 

However, I have the problem that if the work cycle is too short to display the work page (less than 100ms), the page is displayed, the value of the status variable is "ready," and the observer doesn't catch the change, so the work page remains displayed.

How can I strengthen the link between the page and the variable? The goal is to prevent the page from being displayed when the status is anything other than "working".

Furthermore, to start the work cycle from digital inputs, I have created observers in the "base" page (the one that loads when the application starts and above which various parameter settings pages are displayed) linked to the input properties. This allows me to start the cycle from any page.

Considering the suggestion in the previous ticket ("Update ValueDisplay Outlet from another page"), is it better to handle the start commands as a system event instead of using PropertyObservers? Below is an image of the Default page (which loads the FirstPage at startup) with the Start1Slot code called by the Start1Observer.

 

Thank you for support!

1 Answer

0 votes
by
Hello Nicola,

Yes, I would use system events for this. The events ensure that every trigger is sent. Conversely, notifications for property observers can accumulate if the value changes very quickly. In other words, property observers don't guarantee that every value change will trigger individual notifications. They are guaranteed to arrive after the value changes. However, the value can change multiple times in the meantime.

To avoid that we have overlooked another cause of the issue, I would suggest to make a test with only one System Event in the Device class. The event should be triggered when alternation from Working to Ready state occurs. Then add System Event Handler to the respective page and when the event arrives, dismiss the page. In the second pass, if this modification worked as expected, I would add other System Events for other state alternations and adapt all respective pages.

I hope it solves the issue.

Best regards

Paul Banach
by

Hello Paul,

I tried the solution of the System Event, but it still doen't work if the working cycle is too short.I think this happens because the working->ready state change event occurs before the work cycle page is actually displayed, and the event handler for this state change is handled on the work cycle page.

I need to understand if it's possible to handle the event globally (i.e., on all pages), so that when the working->ready state change event occurs, I can check whether the page is displayed or in the display queue and remove it. 

Or, I think an alternative might be to manage the display with a property (e.g., DisplayWorkingPage) that, if true, calls the PresentDialog function and, if false, the DismissDialog function. Would this be possible? I was thinking of creating this property in the "device" class.

Also, does the DismissDialog function remove the page even if it isn't actually displayed yet (i.e., in the display queue)? I mean, what if I call the PresentDialog and after a few milliseconds (before the page is actually displayed) the DismissDialog, does it removes the page from the queue?

I'm waiting for your suggestions about how to better handle this.

Thanks for support.

by

Hello Nicola,

I need to understand if it's possible to handle the event globally (i.e., on all pages), so that when the working->ready state change event occurs, I can check whether the page is displayed or in the display queue and remove it. 

you can have multiple System Event Handlers existing at the same time in all pages. If the event is triggered, all affected handlers are notified.

Or, I think an alternative might be to manage the display with a property (e.g., DisplayWorkingPage) that, if true, calls the PresentDialog function and, if false, the DismissDialog function. Would this be possible? I was thinking of creating this property in the "device" class.

Technically it is possible. Essential would be that PresentDialog() or DismissDialog() are called when the state of the property changes.

Also, does the DismissDialog function remove the page even if it isn't actually displayed yet (i.e., in the display queue)? I mean, what if I call the PresentDialog and after a few milliseconds (before the page is actually displayed) the DismissDialog, does it removes the page from the queue?

Both operations PresentDialog() and DismissDialog() don't show/hide the affected components immediately. There is always some delay. If dialogs are presented with animations, the delays can be even larger. If you invoke PresentDialog() and then DismissDialog() still before the present operation is finished, the present operation is canceled again.

I hope it helps you further.

Best regards

Paul Banach

by

Hello Paul,

thank you for support.

I decided to create a "global" event handler on the main application page (i.e. the Class Core::Root) to handle page present/switch and dismiss.

But I have a problem when I try to dismiss the dialog from the slot method called by the "global" event handler. The prototyper stops and got the exception "The dialog component is actually not presented"

 

To make you better understand the problem, I'm going to explain the situation in details.

I have decided to handle dialog switch and presentation from the main application page as done in the examples regarding "Managing dialogs (multiple screens)" online guide.

My application, at the moment, is quite simple and consists of two pages (for parameters setting) that can be switched, and a workingcycle page that can appear either via an external signals or by pressing buttons on the screen.

I have created a slot property on each page to display the other page and associate the relevant slot method with it.

So, at the beginning, I present FirstPage on the Init()method of main application page:

var Application::FirstPage FirstPageDialog = new Application::FirstPage;
//Associate slot method to switch to second page
FirstPageDialog.OnDisplaySecondPage = DisplaySecondPage;
//Present First page
rootthis.PresentDialog( FirstPageDialog, null, null, null, null, null, null, null, null, false );

to switch from first to second page, the DisplaySecondPage slot method is: 

var Application::SecondPage SecondPageDialog = new Application::SecondPage;
//Associate slot method to swicth to second page
SecondPageDialog.OnDisplayFirstPage = DisplayFirstPage;
//Switch to second page
rootthis.SwitchToDialog( SecondPageDialog, null, null, null, null, null, null, null, null, null, false );

The same, to switch form second to first page the DisplayFirstPage slot method is:

var Application::FirstPage FirstPageDialog = new Application::FirstPage;
//Associate slot method to swicth to second page again
FirstPageDialog.OnDisplaySecondPage = DisplaySecondPage;
//Switch to first page
rootthis.SwitchToDialog( FirstPageDialog, null, null, null, null, null, null, null, null, null, false );

To present the CyclePage, all "triggers" (both from button and property change observer linked to a digital input) pass through the following slot method:

var Application::CyclePage CycleDialog = new Application::CyclePage;
//Present Cycle Page
rootthis.PresentDialog(CycleDialog, null, null, null, null, null, null, null, null, null);

The "global" event handler is triggered from the change of the propery Status from 5 (working) to 4 (ready).

To dismiss the page, the slot method linked to the OnEvent of the EventHandler is:

rootthis.DismissDialog( this, null, null, null, EnableObservers, null, false );

This handling should ensure that the slot method to dismiss the working page dialog is always executed regardless of the page displayed. But I don't understand why I'm getting this error, and I also don't understand how to set up checks to see if the currently displayed page is the cycle page.
I saw the IsCurrentDialog command, but how can I use it outside of the cycle page to determine if the displayed page is the cycle page?
What I think is "missing" is how, once the CyclePage has been created, I can access it outside of the method that presents it.
I'm attaching a screenshot of the mail page, with the following methods called:

- Start slot present the cycle page and every StartXSlot,StartXExtSlot and StartXBtnSlot postsignal this method (described above);

- NotWorkingEventHandler has slot CloseWorkPage linked OnEvent, and CloseWorkPage has the dismiss command (descrived above);

- DisplayFirstPage / DisplaySecondPage slot methods are described above.

Sorry if I insist on this, but I have to admit that closing a page due to an external event is creating me several problems that I can't understand and solve!

Thank you again for support.

 

by

Hello Nicola,

based on the provided information it is difficult to deduce what exact is wrong. In which context is following code executed? The reported error message indicates that 'this' is not actually presented as dialog. The first parameter should refer the dialog you want to dismiss.

rootthis.DismissDialog( this, null, null, null, EnableObservers, null, false );

Other idea: are there multiple handlers reacting to the same event? In such case it could be convenient to add IsCurrentDialog() condition to the event handler to avoid the execution of the code if the respective component receiving the event is not presented actually.

I hope it helps you further. If not, you can prepare a (very, very simple) example demonstrating the issue and upload it here for further analysis.

Best regards

Paul

by

Hello Paul,

I have created a quite simple example about current dialogs management (or how I'm trying to do).

My goal is to close the cycle page outside the context of the cycle page, since the closing of the page is caused by the change of value of a variable and therefore it is not certain that the page is actually displayed (it could still be waiting to be displayed, i.e. in the queue).

In the online examples, it seems that closing the page is always associated with a button. In my case, I'd like it to also be associated with a system event (which I haven't included in the current example).
In the example, I've included the option to display the work cycle page via a button or external signal (device property change).
As you can see, the exception occurs when closing the page, both when the countdown expires and when the STOP button is pressed.
I hope this helps you understand what I'm doing wrong.

Here you find the example:

Dialogs example

Thank you for support.

Nicola

by

Hello Nicola,

thank you for providing the example. I was able to reproduce two error cases: (1) when clicking on the 'Start Cycle' button and (2) when clicking on the 'Stop' button.

Error 1. After clicking 'Start Cycle' button:

The error is understandable. You invoke DismissDialog( this, ... ) in context of the Init method. At this time, the component in question has not yet finalized its initialization. It is even not yet part of the view tree and it is not yet dialog. It is under construction. This explains the error message.

I suppose you want the dialog to not appear if some condition at the presentation time of the dialog is true. Is my assumption correct?

If yes, do following:

1. Perform the test immediately before presenting the Cycle Page dialog in the method ShowCyclePage. In other words, if at presentation time is already known that the dialog is not necessary, don't present it.

2. If the condition was false at presentation time of the dialog, perform the regular PresentDialog() operation.

3. If shorty after this operation an even arrive (or some condition is fulfilled) notifying you that the dialog Cycle Page is not necessary anymore, perform DismissDialog() operation now. Hier you have two possibilities:

Option 1: Let the Cycle Page itself react to the event causing its own dismiss operation.

Option 2: You handle the event or evaluate the condition outside the Cycle Page dialog, then you have first to find the instance of Cycle Page presented recently and then perform DismissDialog() with it as parameter. To find an instance use the method FindDialogByClass(). Following could be the code to find the actually presented Cycle Page dialog and dismiss it:

var Core::Group dialog = rootthis.FindDialogByClass( Application::CyclePage );

if ( dialog )
  rootthis.DismissDialog( dialog, null, null, null, null, null, false );

Error 2. After clicking 'Stop' button:

In this case you execute the code DismissDialog( this, ... ) inside the method ExitCyclePage. The method ExitCyclePage is implemented in the class Application::DefaultPage. The parameter 'this' refers thus the instance of Application::Default. Your implementation is trying to dismiss Default Page instead of Cycle Page.

Option 1: The possibly simplest would be to move the method ExitCyclePage to the Cycle Page component. When the user presses the button stop, the Cycle Page own method is executed and the code rootthis.DismissDialog( this, ... ) is correct since this represents 'this' instance of Cycle Page.

Option 2: if the ExitCyclePage method has to exist in Default Page, then use again the method FindDialogByClass() (as described above) to find the instance of the dialog in question and dismiss it. For demonstration purpose I attach a screenshot of the method after the modification:

I hope it helps you further.

Best regards

Paul Banach

by
Hello Paul,

now everything is clear!

What I was missing was how to close the dialog outside the CycleDialog page and now everything works.

Thank you very much for support!

Nicola

Ask Embedded Wizard - Archive

Welcome to the Ask Embedded Wizard archive. This community forum served us well for many years, but we've evolved our support approach!

Your resources:

The Embedded Wizard Online Documentation provides comprehensive documentation, tutorials, examples and ready-to-use software packages.

For dedicated assistance, explore our Embedded Wizard Product Support.

You can still browse the valuable discussions from our community history here.

Embedded Wizard Website | Privacy Policy | Imprint

...