cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 
2024 Technology Preview Program

2024 Technology Preview Program:
Master powerful new features and shape the latest BIM-enabled innovations

Archicad C++ API
About Archicad add-on development using the C++ API.

how to let the user stop a for-loop (DGModelessHandler?)

Anonymous
Not applicable
Hi!

I am searching for a way to enable the user to stop a for-loop started by the addon.

Example:
 
{ 
   bool abort = false; 
   CTestWindow window(abort); //modeless window with access to "abort" 
   for (int i = 0; (i < 10) && !abort; i++) 
   { 
      Sleep(500); 
      PRINT(GET(i)); 
   } 
} 


The user should be able to stop the for-loop via the created modeless-window.
But the window is not responding during the for-loop.

I tried to use the objectorientated way (inheriting or using DG::ModelessDialog) and the old way
(using DGCreateBlankModelessDialog, DGBeginProcessEvents, ...).

I found the docu of "DGModelessHandler" which i do not understand.
This method seems to belong to the 'old way'.
In the docu there is a passage which describes the scenario i am facing:

> The usage of the dialog status flag can be explain by the following
> example. Suppose that an application begins a long process such as a
> time-consuming calculation. In this case, it is advisable to show a
> process indicator dialog with a Stop button until the calculation is
> finished. When handling system-specific messages during this period,
> you should call DGModelessHandler with onlyUpdate of value true. This
> ensures that modeless dialogs of DG_DS_NORMAL state do not handle any
> messages except the update messages (this way they do not respond to
> any user actions). The status of the process indicator dialog should
> be set to DG_DS_ENABLED in order to enable the user to press the Stop
> button and cancel the process.

But i have no idea how to use "DGModelessHandler" 😞

It seems that this method does not belong into the code of an Addon:
> Call this function just after receiving a system-specific message
> in the application's message loop.

This method is used in the following file of the api-examples:
"...\API Development Kit 5\Examples\DGTest\Sources.win\DGTestWin.cpp"

This does not look like an Addon but like an application.

Hmm, very strange.

I hope someone has a hint how to start a modeless dialog which keeps responding.
I would prefer the object orientated way using the class DG::ModelessDialog.

Perhaps there is a ArchiCad-way to use threads?

The process-window which could be created with "ACAPI_Interface(APIIo_InitProcessWindowID,.." does not seem to work properly.

regards,
John
4 REPLIES 4
Akos Somorjai
Graphisoft
Graphisoft
Hello John,

Try to use a palette instead. Modeless dialogs are special to ArchiCAD.

To make the loop breakable try calling something like DGGetMousePosition inside the loop; that lets other event handlers run.

The example you are referring to is the test application for the Dialog Manager (a.k.a DG) module.

HTH,

Akos
Anonymous
Not applicable
Hi Akos 🙂
Akos wrote:

Try to use a palette instead. Modeless dialogs are special to ArchiCAD.


Ok, now my test class creates a palette the old way (DGCreateBlankPalette) and looks like that:
 
class CTest3Window 
   { 
   public: 
      CTest3Window(); //creates a pallette with "DGCreateBlankPalette" 
      ~CTest3Window(); 
      void live(); //invokes "DGGetMousePosition" 
      static short int DGCALLBACK mainEventHandler( 
         short  message, 
         short  dialId, 
         short  itemId, 
         long   userData, 
         long   msgData 
         ); 
   private: 
      short mDialogId; 
   }; 

   CTest3Window::CTest3Window() 
   { 
      mDialogId = DGCreateBlankPalette   ( 
         200,                   //   hSize, 
         200,                   //   vSize, 
         DG_DLG_NOGROW,         //   growFlag, 
         DG_DLG_CLOSE,          //   closeFlag, 
         0,                     //   captionFlag, 
         DG_DLG_NORMALFRAME,    //   frameFlag, 
         this->mainEventHandler,//   dCallBack, 
         0                      //userData 
         ); 
      DGShowModelessDialog(mDialogId); 
      DGBeginProcessEvents(mDialogId); 
      DGSetModelessDialogStatus(mDialogId, DG_DS_ENABLED); 
   } 
    
   CTest3Window::~CTest3Window() 
   { 
      DGEndProcessEvents(mDialogId);       
      DGDestroyModelessDialog(mDialogId); 
   } 
    
   void CTest3Window::live() 
   { 
      DGMousePosData mpd; 
      short mouseStatus = DGGetMousePosition(mDialogId, &mpd); 
   } 
   


To make the loop breakable try calling something like DGGetMousePosition inside the loop; that lets other event handlers run.


In CTest3Window::live() I invoke "DGGetMousePosition" and the for loop looks like that:
for (int i = 0; (i < 10) && !abort; i++) 
{ 
  window.live(); //invokes "DGGetMousePosition" 
  Sleep(500); 
} 


The eventhandler assigned to the palette is still not invoked during the for-loop 😞
I tried the the same going the object-orientated way using DG::Palette, but the attached Event-Observer is not notified neither.
The example you are referring to is the test application for the Dialog Manager (a.k.a DG) module.

Yes, this does not seem to be an addon.
Do you have an idea how to use DGModelessHandler in a normal addon?

Greeting,
John
Anonymous
Not applicable
Hi 🙂
John wrote:


The process-window which could be created with "ACAPI_Interface(APIIo_InitProcessWindowID,.." does not seem to work properly.



Now, I am using "APIIo_InitProcessWindowID" after all...
It takes a long time before the process-window appears.
So if the process does not takes long enough you don't see this window. Thats why i thought it would not work.
 
bool abort = false; 
short nPhase = 1; 
long maxval = 100; 
 
ACAPI_Interface (APIIo_InitProcessWindowID, "incremental search", &nPhase); 
ACAPI_Interface (APIIo_SetNextProcessPhaseID, "working...", &maxval); 
 
for (int i = 0; (i < maxval) && !abort; i++) 
{ 
   Sleep(500); 
   PRINT(GET(i)); 
   ACAPI_Interface(APIIo_SetProcessValueID, &i, NULL); 
   if (ACAPI_Interface(APIIo_IsProcessCanceledID, NULL, NULL)) 
   { 
      abort = true; 
   } 
} 
ACAPI_Interface (APIIo_CloseProcessWindowID, NULL, NULL); 


[Threads]
An other way i tried was the following:
I coded a Status-Window which is able to invoke the method whose status is to be monitored in a new thread.
But there were several problems:
1. The thread I used is not OS-independent and I think you should avoid OS-specific code in AC-Addons.
2. ArchiCAD often crashes, if the method running in the thread is selecting elements or changing the AC-database.

But perhaps someone still has a hint how to start a window which keeps reacting while an other method is working.

cu,
John
Ralph Wessel
Mentor
John wrote:
Now, I am using "APIIo_InitProcessWindowID" after all...
It takes a long time before the process-window appears.
So if the process does not takes long enough you don't see this window. Thats why i thought it would not work.
John,

The API process window has a built-in delay before appearing, which unfortunately cannot be modified. The delay is far too long in my opinion - a user could easily think the software has crashed.
John wrote:

[Threads]
An other way i tried was the following:
I coded a Status-Window which is able to invoke the method whose status is to be monitored in a new thread.
But there were several problems:
1. The thread I used is not OS-independent and I think you should avoid OS-specific code in AC-Addons.
2. ArchiCAD often crashes, if the method running in the thread is selecting elements or changing the AC-database.
The crux of the problem is that much of ArchiCAD has not been designed to support threading. That's why it crashes when a separate thread attempts to access the database.
Ralph Wessel BArch
Software Engineer Speckle Systems