Reputation: 137
Not sure this possible, may be not, but I'm asking. I have a Timage where I'm drawing shapes inside, using Timage.canvas This is achieved by a user thread, not the main's application thread, because the main thread is (unfortunately for historical reasons) dealing with another synchronous process that makes stop it from processing messages. I can do image.lock / unlock, writing inside, but the image does not refresh. Seems only to be repainted when mai's thread can process messages. I cannot use synchronize method either. May only main application thread is allowed to refresh graphic objects ?
Upvotes: 0
Views: 1249
Reputation: 596592
the main thread is (unfortunately for historical reasons) dealing with another synchronous process that makes stop it from processing messages.
You need to change that design. Historical or not, it is just plain wrong to block the main UI thread for any noticeable amount of time. Whatever the main thread is doing synchronously, that logic needs to be rewritten - either changed into an asynchronous process if it must remain in the main UI thread (in case it relies on anything that has thread affinity), or else moved to a separate worker thread that it can block and then notify the main thread of updates when needed.
Until you can fix that design properly, at least see if you can update the synchronous process to call Application.ProcessMessages()
periodically. The main thread needs to be able to process OS messages in a timely manner, or the OS is going to flag the process as unresponsive, grey/blur the UI, and the user is likely to kill it.
Seems only to be repainted when mai's thread can process messages.
That is correct. Specifically, when the TImage.Parent
control processes a WM_PAINT
message (TImage
is a TGraphicControl
descendant, so it does not receive WM_PAINT
messages directly from the OS, but rather delegated from its Parent
control).
I cannot use synchronize method either.
Once you fix the broken design that is blocking the main thread, then you will be able to use TThread.Synchronize()
, TThread.Queue()
, SendMessage()
, PostMessage()
, etc. Any worker thread that needs to touch the UI must sync with the main thread.
May only main application thread is allowed to refresh graphic objects ?
Yes. What you should do is change the synchronous process into an asynchronous one, and then have your drawing thread draw onto an in-memory TBitmap
(you still need to use the Canvas.Lock()
/Unlock()
methods to protect shared GDI resources) and then synchronize with the main UI thread to draw that TBitmap
onto the TImage
when needed.
If you cannot change the synchronous process into an asynchronous one, then at least see if you can update the synchronous process to check the drawing thread periodically for an updated TBitmap
, grab it and draw it onto the TImage
, and then force an immediately UI redraw via the TImage.Refresh()
method.
Upvotes: 2
Reputation: 16045
Windows repainting is made by Windows itself (GDI layer) when it send appropriate messages to the window's main thread.
In these special states Windows creates special 'device context' (Paint DC) that allows to draw the windows on the screen. Those special PaintDCs take care about things like clipping (when window is covered by another one or menu, or is partially outside the desktop, etc). Outside of those messages (and lacking Paint DC) handling repainting of the window would not work properly.
So you have to think about unloading Main VCL Thread from long operation.
There are possible hacks though - like you can create a special window in an external thread, that would float above your main window and show the image. But this window you would have to create using raw Windows GDI API and avoiding use of VCL. Okay, Andy (I think that was he) tried to adopt VCL to multithread working and made an article named something about Dialog Windows and Fibers - but that was proof of concept and was so limited and fragile that was not a practical solution
That most probably would be more complex and more error-prone than going normal way and offloading main thread.
Another hack would be inserting Application.ProcessMessages
calls inside your long task - but that is dirty and quite dangerous practice, that might cause - unless you keep very strict and fine-grained control of different messages - infinite recursion or some other unexpected mix of events. Again, that is a dangerous and unreliable approach.
Upvotes: 1
Reputation: 613013
The image is painted in response to a WM_PAINT
message. That message is delivered to the main thread. Which you have blocked. Hence the problem.
The only solution is to fix the broken design. Don't block the main thread. Move long running tasks onto worker threads so that the main thread can pump its message queue and so remain responsive.
Upvotes: 1