Progress Bar status indicator

Discussions related to Visual Prolog
Loffy
Active Member
Posts: 47
Joined: 15 Aug 2019 11:32

Re: Progress Bar status indicator

Unread post by Loffy » 16 Oct 2019 7:00

Martin,

I have changed a few things as I wanted to output a text message rather than, for example, the current time.

The first 10 characters of my message shows on the bottom toolbar where the timer used to show.

To achieve this I created a fact to store my message at a time that was convenient for my code, and then modified your "OnTimer" code as follows to retrieve the text message for display:

Code: Select all

predicates     onTimer : window::timerListener. clauses     onTimer(_Source, _TimerID) :- %        Now = time::now(),         getmessage(Text),         vpiToolbar::setValue(getVpiWindow(), resourceIdentifiers::idt_1, vpiToolbar::text_value(Text)),         !.        
So, everything is ready except that the display area for the message is restricted to the length of the original timer display.

I have tried implicitly nominating the length of the text value and get a compile error:

e504 The expression has type '::string', which is incompatible with the type 'object' TaskWindow.pro TaskWindow\

Any hints to make the toolbar accept any text up to say 80 characters in length?

Regards,

Loffy

User avatar
Thomas Linder Puls
VIP Member
Posts: 1625
Joined: 28 Feb 2000 0:01

Re: Progress Bar status indicator

Unread post by Thomas Linder Puls » 16 Oct 2019 8:44

If you have created and attached a status bar as described above you should remove it again.

A standard GUI project comes with a status bar:

Code: Select all

implement projectToolbar     open vpiDomains, vpiToolbar, resourceIdentifiers   class facts     statusCell : statusBarCell := erroneous.   clauses     create(Parent) :-         StatusBar = statusBar::newApplicationWindow(),         statusCell := statusBarCell::new(StatusBar, 0),         StatusBar:cells := [statusCell],         Toolbar = vpiToolbar::create(style, Parent, controlList),         setStatusHandler(Toolbar, displayStatus).   clauses     displayStatus(Text) :-         statusCell:text := Text.
You can add more statusBarCells if you like, see for example in the sciLexerStatusBar

Code: Select all

implement sciLexerStatusBar inherits statusBarControl   clauses     new(Parent) :-         statusBarControl::new(Parent),         Cell_0 = statusBarCell::new(This, statusBarCellWidth),         cell_position := statusBarCell::new(This, statusBarCellWidth),         cell_modified := statusBarCell::new(This, statusBarCellWidth),         cell_insert := statusBarCell::new(This, statusBarCellWidth),         cells := [Cell_0, cell_position, cell_modified, cell_insert].
This code is "inside" a dedicated statusBarControl class, in the project case it would look like this:

Code: Select all

implement projectToolbar     open vpiDomains, vpiToolbar, resourceIdentifiers   class facts     cell_position : statusBarCell := erroneous.     cell_modified : statusBarCell := erroneous.     cell_insert : statusBarCell := erroneous.   clauses     create(Parent) :-         StatusBar = statusBar::newApplicationWindow(),         Cell_0 = statusBarCell::new(StatusBar, statusBarCellWidth),         cell_position := statusBarCell::new(StatusBar, statusBarCellWidth),         cell_modified := statusBarCell::new(StatusBar, statusBarCellWidth),         cell_insert := statusBarCell::new(StatusBar, statusBarCellWidth),         StatusBar:cells := [Cell_0, cell_position, cell_modified, cell_insert],         Toolbar = vpiToolbar::create(style, Parent, controlList),         setStatusHandler(Toolbar, displayStatus).
To set the texts you will need to export predicates or (perhaps better) properties.

Code: Select all

class projectToolbar ... properties     position : string. ... end class projectToolbar
with a suitable implementation:

Code: Select all

clauses     position(T) :-         cell_position:text := T.   clauses     position() = cell_position:text.
I think we will change the default project structure for this, because it is somewhat strange that the status bar is part of the projectToolbar class.
Regards Thomas Linder Puls
PDC

User avatar
Thomas Linder Puls
VIP Member
Posts: 1625
Joined: 28 Feb 2000 0:01

Re: Progress Bar status indicator

Unread post by Thomas Linder Puls » 16 Oct 2019 9:24

To have a progressBarControl in one of the cells, you will do something like this (this is from the IDE code and there is probably things that you don't need):

Code: Select all

    facts         progress_ctl : progressBarControl := erroneous.       clauses         progressBarNew(Message, MaxCount) = ProgressBarId :-             ProgressBarId = progressBarId + 1,             progressBarId := ProgressBarId,             countItem := 0,             countItemMax := MaxCount,             progressName := Message,             progress_ctl := progressBarControl::new(cToolbarsVDE::statusBar),             progress_ctl:useTaskBarList := true,             if 0 = MaxCount then                 progress_ctl:marquee_style := true             else                 progress_ctl:setRange(0, MaxCount)             end if,             progress_ctl:show(),             cToolbarsVDE::statusBarCell2_progress:control := core::some(progress_ctl),             if 0 = MaxCount then                 progress_ctl:autoMarqueeMode := true,          ...

The important parts are these

Code: Select all

            progress_ctl := progressBarControl::new(cToolbarsVDE::statusBar),             ...             progress_ctl:show(),             cToolbarsVDE::statusBarCell2_progress:control := core::some(progress_ctl),
  • Create a progressBarControl with the statusBar control as parent
  • show() it
  • attach it to the relevant statusBarCell (to get it resized and positioned).
Regards Thomas Linder Puls
PDC

User avatar
Thomas Linder Puls
VIP Member
Posts: 1625
Joined: 28 Feb 2000 0:01

Re: Progress Bar status indicator

Unread post by Thomas Linder Puls » 16 Oct 2019 9:58

You should notice that it is difficult to deal with progress bars and long running operations.

First of all the GUI thread must be active. If/while the GUI thread is performing long calculations the progressBar (and all other GUI) is frozen, and Windows will eventually mark the application as "not responding". Displaying GUI requires an active GUI thread, that handles the GUI messages.

So progress bars are only useful when you master to put the lengthy operations into the background, while leaving the GUI thread active. This can be done in several ways (none of which are easy to deal with):
  • You can start additional threads
  • You can use the threadpool
  • You can chop-up your task in smaller pars that you "postAction" to a window
  • ...
  • You can call vpi::processEvents
The last seems to be the simplest, but it has its problems and in the end it is not a good solution.

Secondly, there can be some problems with ensuring that you don't get many progress bars (in the same cell), because many tasks are put into the background simultaneously. The "first level" bug is to allow to start the same/similar task before the current one is completed. You many notice in the IDE that several menu items are disabled while the IDE is building the project (so that you cannot start several builds simultaneously).

Thirdly, there can be some problems with removing the progress bar again, not in the normal case, but in the case where the task ends abnormally (due to for example an error).
Regards Thomas Linder Puls
PDC

Loffy
Active Member
Posts: 47
Joined: 15 Aug 2019 11:32

Re: Progress Bar status indicator

Unread post by Loffy » 17 Oct 2019 8:23

Thomas,

Thanks for the above posts and all of the work you put into this.

For the moment I will go to something a bit crude to give some very basic info, and come back to the problem after I have developed more of the fundamental aspects of my project idea. Good progress so far; a few more things to prove.

I thank both yourself and Martin for your constructiveness.

Regards,

Loffy

Loffy
Active Member
Posts: 47
Joined: 15 Aug 2019 11:32

Re: Progress Bar status indicator

Unread post by Loffy » 17 Oct 2019 11:57

Thomas,

I have tried my "crude" approach, though I have hit a hurdle that I think relates to background processing of stdio::write functionality.

Firstly I send a message to the standard stdio outstream at the beginning of the process to the message window to inform the user to be patient. Then I setup for writing a large test file via an file outstream process and write the file. The file process works fine. After that I redirect the stdio outstream back to the standard message window.

The problem is that the message telling the window to wait does not arrive until after the file writing process has completed.

Please see code below:

Code: Select all

 Start = time::getTickCountInMsec(),         stdio::write("User file creation in progress. This will take a few minutes. Please wait until completion message appears..... \n"),         Restore = stdio::outputStream,         Restore:flush(),         OutStream = outputStream_file::createUtf8("UserFileTest.txt"),         stdio::outputStream := OutStream,         Index = 0,         Limit = 2 ^ 31,         streamloop(Index, Limit, MenuTag),         OutStream:flush(),         OutStream:close(),         End = time::getTickCountInMsec(),         Elapsed = (End - Start) div 1000,         stdio::outputStream := Restore,         stdio::write("Stream file elapsed time = ", Elapsed, " seconds\n"),         !.
I have done a reasonable amount of searching for information on the "background" stdio writing to the message window including seeing references to "mutex" and "monitorQueue", though I have not yet found a way to "flush" my message properly before the file process grabs control of the stdio file outstream process.

The answer is probably simple, it is just that I have not yet found it.

Regards,

Loffy

B.Hooijenga
VIP Member
Posts: 69
Joined: 11 Jul 2002 23:01

Re: Progress Bar status indicator

Unread post by B.Hooijenga » 17 Oct 2019 13:59

hello Loffy,

please try: vpi::processEvents immediate after write command
like:

Code: Select all

        msg:write("Busy with backup ...\n"),         _ = vpi::processEvents(),         ...         rest of code
I had no troubles so far.

regards
Ben

Loffy
Active Member
Posts: 47
Joined: 15 Aug 2019 11:32

Re: Progress Bar status indicator

Unread post by Loffy » 18 Oct 2019 7:01

Ben,

Thanks, that works. I took a few seconds for the message to appear, but that is a lot better than not appearing until after the file has been written.

Regards,

Loffy

Harrison Pratt
VIP Member
Posts: 285
Joined: 5 Nov 2000 0:01

Re: Progress Bar status indicator

Unread post by Harrison Pratt » 18 Oct 2019 11:33

Calling vpiProcessEvents/0 frequently in a long operation does slow down the operation significantly, but it may be worth it to display messages quickly enough to be useful. I have used a counter and called vpiProcessEvents/0 every "Nth" time to minimize unnecessary overhead.

Would invalidating the messageControl in the messageForm work to force the display to update? That would require saving the messageForm from taskWindow:onCreate/3 so it can be accessed from other classes.

Martin Meyer
VIP Member
Posts: 290
Joined: 14 Nov 2002 0:01

Re: Progress Bar status indicator

Unread post by Martin Meyer » 20 Oct 2019 19:38

I have tried to set up an example which follows all of Thomas' advice.

In the example the progressBarControl is working fine in formWindows, but there is a problem with it in the status bar.

Please have a look, Thomas. What is wrong about the example?
Attachments
Progress Example 1.zip
(23.18 KiB) Downloaded 6 times
Regards Martin

User avatar
Thomas Linder Puls
VIP Member
Posts: 1625
Joined: 28 Feb 2000 0:01

Re: Progress Bar status indicator

Unread post by Thomas Linder Puls » 21 Oct 2019 11:41

The problem in Progress Example 1 is caused by a bug in statusBarCell.

But you can circumvent the bug by changing the order of the operations:

Code: Select all

implement projectToolbar ... clauses     create(Parent) :-         StatusBar = statusBar::newApplicationWindow(),         StatusCell = statusBarCell::new(StatusBar, statusBarCellWidth),         stateCell := statusBarCell::new(StatusBar, statusBarCellWidth),         ProgressCell = statusBarCell::new(StatusBar, statusBarCellWidth),         progress_ctl := progressBarControl::new(StatusBar),         progress_ctl:show(),         StatusBar:cells := [StatusCell, stateCell, ProgressCell], %This before         ProgressCell:control := some(progress_ctl), % This after         Toolbar = vpiToolbar::create(style, Parent, controlList),         setStatusHandler(Toolbar, { (Text) :- StatusCell:text := Text }).
The problem has to do with attaching the control before the cells has been set on the statusBar. We have never experienced this problem because we would never have a progressBarControl from start to end. We create the progressBarControl when the lengthy operation starts and destroy it again when it ends.

We update the progress directly from the lengthy operations (i.e. when they make progress) rather than having a timer ticking.
Regards Thomas Linder Puls
PDC

Martin Meyer
VIP Member
Posts: 290
Joined: 14 Nov 2002 0:01

Re: Progress Bar status indicator

Unread post by Martin Meyer » 21 Oct 2019 18:14

Thank you Thomas! Now the progressBarControl works on the statusBar in the example.

Updating the progress directly from the lengthy operations is of course much better than polling for progress updates. But I do not see how to do it.

Simply calling Windows GUI predicates from the lengthy operations is not allowed because the Windows GUI is not thread safe. Delivering the progress data via a monitorQueue is no solution because it only shifts the problem to the question, how to dequeue data from the monitorQueue without polling.

I thought, the solution could be some (thread safe) "threadHasSentData" GUI event. But I have not found an event of such kind.

Please clue me how to update the progress display directly from the lengthy operations.
Regards Martin

User avatar
Thomas Linder Puls
VIP Member
Posts: 1625
Joined: 28 Feb 2000 0:01

Re: Progress Bar status indicator

Unread post by Thomas Linder Puls » 22 Oct 2019 9:32

Actually there is no threading problem with the method that updates the progress of a progressBarControl because that method uses SendMessage function which is thread safe.

So it it perfectly safe to do like this from any thread:

Code: Select all

clauses     longOperation(...) :-         ... % do something         theProgressBarControl:progress := 10,         ... % do something
But if that was not the case then you could use postAction like this

Code: Select all

clauses     longOperation(...) :-         ... % do something         theProgressBarControl:postAction({ :- theProgressBarControl:progress := 10 }),         ... % do something
postAction posts a message to the window and when the window process the message it performs the corresponding action. Hence the action will be performed by the GUI thread (provided the window still exists when the message is processed).
Regards Thomas Linder Puls
PDC

Martin Meyer
VIP Member
Posts: 290
Joined: 14 Nov 2002 0:01

Re: Progress Bar status indicator

Unread post by Martin Meyer » 23 Oct 2019 22:22

aah :idea:, thank you again, Thomas! I have modified the example to directly update the progress display.

The example has only one kind of formWindow. But a real application could have many kinds of formWindows and each kind needs to display different controls on the status bar. Thus (as always) it is good to follow your advice:
We create the progressBarControl when the lengthy operation starts and destroy it again when it ends
There is a difficulty however to implement your advice which I could not solve. When a formWindow gets the focus, I can add controls on the status bar in a getFocusListener - that's easy. But how to determine when to remove them? To -unconditionally- remove them in a loseFocusListener does not work out.

I suppose, I must determine in the loseFocusListener whether the focus moves to some control inside the formWindow or to some window outside the formWindow. But how to do that?

Probably you have explained it already in another thread but I could not find it. Please help again and clue me how to determine when to remove the controls from the status bar.
Attachments
Progress Example 2.zip
(23.48 KiB) Downloaded 7 times
Regards Martin

User avatar
Thomas Linder Puls
VIP Member
Posts: 1625
Joined: 28 Feb 2000 0:01

Re: Progress Bar status indicator

Unread post by Thomas Linder Puls » 24 Oct 2019 11:39

I consider the application windows status line and its progress bar as an application-wide status bar. So it should indicate application-wide status (for example in the IDE project load and project build). Your application allows several "dealing" tasks to proceed simultaneously, and as such none of them seems to have application-wide status. And therefore I would not choose to reflect one of them on the overall status bar. Depending on the application the overall status bar could reflect the overall/combined status of the total set of such operations. But to say that the status of the application corresponds to the status of the task that has focus seems strange.

In a "large" application with many such possible tasks, you will have decide/consider a lot of things:
  • Which tasks should be allowed to run simultaneously
  • How to prevent tasks from being started if they are not allowed
  • How to keep track of which such tasks are running
  • How to terminate tasks prematurely
  • How to reflect the status of such tasks in the application-wide status
  • How to reflect local status if necessary
  • ...
Many application will only have few such tasks and therefore such decisions will typically be taken ad hoc during development and evolved in small bites based on user experience/complaints rather than undergo a true "design phase".

But if an application is by nature going to deal with lots of lengthy operations then it may be worth while dealing thoroughly with these problems.

It is also an option to use an overall progress bar control as a load indicator rather than a progress indicator.
Regards Thomas Linder Puls
PDC

Post Reply