Discussions related to Visual Prolog
User avatar
Richard Clarke
Posts: 13
Joined: 15 Mar 2012 15:55

Messages window - update

Unread post by Richard Clarke »

Is it possible to update the messages window using :update() ?

Reason for asking is:
Output to the messages window is delayed while looping code loops in a form "formA"
Output to an edit_ctl of another form, "fomB" is similarly delayed but can be forced OK with
FomB:update().

I gather the explanation is -
Force updating of window
The win_update predicate cause any update events that might be pending in the event queue to occur and ensure that the window is completely redrawn"
VPI version 5, 1986-97 [sic]
RSC
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Re: Messages window - update

Unread post by Thomas Linder Puls »

Richard Clarke wrote:Output to the messages window is delayed while looping code loops in a form "formA"
Your analysis is slightly wrong: it is not the message window that is "delayed" it is everything that is delayed. While code loops somewhere no windows messages are handled and hence no paint-handling takes place, and your entire program is unresponsive. If your calculations takes too long Windows will even place fog on your window and write (not responding) in the title bar.

There are several possible solutions:
  • Call vpi::processEvents regularly during calculation.
  • Split the calculation in smaller parts that mixed into the event handling (e.g. using postAction).
  • Run the calculations in a separate thread.
The first is preferable in "simple" cases.

Calling vpi::processEvents is (on surface) the simplest: you simply insert such calls here and there in your calculations (especially after message window output). However, processing events while already processing some event can lead to strange behavior.

Example: the user presses ButtonA which starts the long operation, during the operation the user presses ButtonA again. Without vpi::processEvents the second press will be performed after the first one completes. But with vpi::processEvents the second event will be handled when vpi::processEvents is called, so the second calculation will be performed in the middle of the first. Typically such second events will cause the program to misbehave and should therefore be avoided. So normally you will disable most/relevant controls while entering the long operation.
Regards Thomas Linder Puls
PDC
User avatar
Richard Clarke
Posts: 13
Joined: 15 Mar 2012 15:55

Unread post by Richard Clarke »

Thanks for getting back to me. Not quite EVERYTHING is delayed: some things slip through others do not. Here's the set up.

There are two forms formA and formB plus the messages window on display.
formB has an edit_ctl

In code behind formA there is a looping routine
Within the loop we send text Rem1 to the two title bars, the edit_ctl and the message window, like this:

Code: Select all

%start fragment %get something to display         Heap=memory::getFreeHeap(),         Stack=memory::getFreeStack(),         Rem1=string::format("Flight #% Distance: % Min Distance %, Max Distance % Mean Distance % Stack size: % Heap size: % \n",itcount,round(Distance),round(minDistance),round(maxDistance),round(meanDistance),Stack, Heap),         %test show rem1 all over the shop         stdio::write(Rem1),         setText(Rem1),         formB::set_top_edit_ctl(Rem1),         formB::setPublicText(Rem1), %end fragment
In addition some lines are drawn in formA
Without the insertion of :update

The title bars are updated (both forms)
The lines are drawn
Neither the edit_ctl opr the message window are updated


Now I insert the following code in the loop:

Code: Select all

 FormB=formB::getCurrentForm(),  FormB:update(),
Result:
The title bars are (still) updated (both forms)
The lines are (still) drawn
The edit_ctl is now updated but the message window is still not.
Therefore (I think) if I could just update() the message window, it too would be updated.
This would surely be BETTER than allowing all events to be processed because we would not want unnecessary event to be processed eg naughty mouse clicks or button presses. So how DO update() the messages window? Where is it at and does it have a handle?

It would be interesting to know how to do that multi thread processing that you mention
RSC
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

Drawing takes place when the draw code is called. And other things may also draw or partially draw directly when things happen. But in most cases drawing takes place on a paint event (WM_PAINT), this is because windows may need to be refreshed for many reasons. When you for example resize a window it will be invalidated and on the next WM_PAINT event the application should redraw the window in its new size.

Drawing out side WM_PAINT can make sense when you want to have some immediate GUI feedback for example when dragging a window divider, but the exact same drawing should also take place in the WM_PAINT event otherwise you may see that the divider disappears again.

Anyways, the problem is still the same the message window and most other controls and windows are (only) drawn in the WM_PAINT event and unless such one is handled no drawing will take place.

Therefore (and for many other reasons) event handling should be short so that other events will also be handled. But the WM_PAINT event is even more "delicate" Windows places this event on an applications event queue automatically, but only when the queue becomes empty. The idea is the application handles all kinds of events (mouse, keyboard, resize, etc.) and when things "settle" it refresh the graphical appearance of the window to reflect the changes caused by the various events.

Threads

Working with threads is both very simple and quite difficult, in the sense that it is very simple to create a new thread that does something it parallel with the other threads in the program and quite difficult because different threads can easily mess things up for each other.

In Windows nearly all GUI routines may only be called by the GUI thread, i.e. by the thread that run the message loop (aka message pump). The exceptions to this rule falls in two categories:
  • Any thread can post a message to a window
  • Certain (few) operations have a clear purpose to deal with "foreign" windows, but often they are just meant to locate such windows so that you can post a message to it.
So if you have a thread (other than the GUI thread itself) that want to influence the GUI, then it will have to do it indirectly by informing the GUI thread that the update should take place. I.e. the update will have to be shifted from the non-GUI thread to the GUI thread. Such shifting will have an asynchronous character, the non-GUI thread will "ask" for an update and eventually/later the GUI thread will perform it. If the non-GUI thread need to wait for the update to be effectuated you will have to use some other means to synchronize back from the GUI thread to the non-GUI thread.

The code you present does not make sense to put into a separate thread, because the code does very little except updating the GUI, and that have to be shifted back to the GUI thread anyway.

The code fragment you show should not run with highest possible intensity, it would seem silly to spend CPU cycles on running this code in a tight loop. So what I would suggest is to run this loop as a "delayCall" loop:

Code: Select all

predicates     updateStatus() :-         Heap=memory::getFreeHeap(),         Stack=memory::getFreeStack(),         Rem1=string::format("Flight #% Distance: % Min Distance %, Max Distance % Mean Distance % Stack size: % Heap size: % \n",itcount,round(Distance),round(minDistance),round(maxDistance),round(meanDistance),Stack, Heap),         %test show rem1 all over the shop         stdio::write(Rem1),         setText(Rem1),         formB::set_top_edit_ctl(Rem1),         formB::setPublicText(Rem1),         delayCall(50, updateStatus).
updateStatus will "update the status" and after 50 milliseconds it will do the same again. delayCall works by means of a WM_TIMER event, so this code will be scheduled into the event handling every 50 milliseconds, all the rest of the time the program can handle other events including WM_PAINT events which will actually make the GUI update the window.

The lines you mention they really should be drawn in the WM_PAINT/onPaint handler.
Regards Thomas Linder Puls
PDC
Post Reply