I have a render loop that I want to run in the background so that I can control the speed of the playback loop making it animate either slow or fast. Right now I'm using sleep and calling begin and end on the CCRenderTexture in the main thread each time I want to use the sprite:
[self performSelectorOnMainThread:#selector(visit) withObject:nil waitUntilDone:YES];
- (void)visit {
[target begin];
[[self.currentLine.brush sprite] visit];
[target end];
}
This was a hack that got it working but of course makes it run very slow. calling begin and end in the background thread just causes nothing to render at all
I want to be able to call begin, do all my drawing in the background thread and then call end but everything I've tried has either done nothing or made my EAGLView flip out.
The CCRenderTexure works by redirecting the opengl output onto itself so that the drawing that gets done is saved on your sprite. This means that in a single operation you need to call begin do all your drawing and then call end.
When running on a background thread the CCDirector is making it's own draw calls in the main thread so that when you call begin and try to do multiple functions in that state the director is drawing too causing all sorts of strange errors.
end needs to be called before normal rendering operations resume.
Related
I'm writing a small Delphi 10.3 VCL Win32 application for viewing a bunch of SVG images. Yes, there must be application that does exactly what I want but I'm using this as a bit of a learning opportunity.
It renders each image and a label indicating the image name so you can quickly scroll through them.
I decided to thread it so the user doesn't need to wait until all the images have been loaded.
This was nice except that the main form isn't painting or processing user "messages" while the loading occurs.
I'm calling the method below via the thread's Synchronize( ) method.
procedure TFinderThread.CreateControlsForNextItem;
var
img: TcxImage;
begin
svg:=TdxSmartImage.Create;
svg.LoadFromFile(m_slFiles[0]);
m_slFiles.Delete(0);
// omitted: TLabel creation
img := TcxImage.Create(m_owner);
img.Left := 0;
img.Top := m_iNextControlTop;
img.Picture.Assign(svg);
img.Parent := m_parent;
m_iNextControlTop:=m_iNextControlTop + img.Height;
svg.Free;
end;
I'd like the main form to be responsive and usable while it's being loaded. I found that a short call to Sleep( ) in the thread's Execute loop makes the UI responsive.
the Execute method contains this loop:
m_iNextControlTop:=0;
while m_slFiles.Count>0 do
begin
if Terminated then
break;
Synchronize(CreateControlsForNextItem);
sleep(1);
end;
The problem
If you use the scroll bar during refresh, some controls don't appear at all in the scroll box. The objects exist, are visible, have the correct parent, etc. This is doesn't change if you later scroll around so it doesn't appear to be a "painting" issue. It's as though the scrollbox doesn't recognize them as content.
If you don't scroll at all during refresh, all the images will appear correctly.
Any idea what's happening?
Thanks!
I need help how to unfreeze my dialog box. I'm using MFC and I have an infinite loop I want to execute when a button is pressed. However, the dialog box freezes when the infinite loop starts. Now I looked at this thread where someone was having a similar problem.
Unfortunately I tried multithreading but I found out that It can't work for me because I'm using an api that uses OLE automation and I'm getting an unhandled memory exception. I think this is because program uses the serial port and i read somewhere you can only use the handle to the serial port in one thread.
My program is simply to see if someone has dialed in to my modem and wait for them to send me a file, then hangup. Here is my loop.
while(1)
{
//get rid of input buffer
ts->_this->m_pHAScript->haReleaseRemoteInput();
ts-> _this->textBox->SetWindowTextA("thread Commence");
//wait for connected
if(success = ts->_this->m_pHAScript->haWaitForString("CONNECT",timeout))
{
//getFile
if(success = ts->_this->m_pHAScript->haWaitForXfer(5000))
{
//hangup
ts->_this->haTypeText("+++ath\r");
}
}
}
Is there a way to unfreeze the dialog box?
Add this code inside while loop:
MSG msg;
while(PeekMessage(&msg, GetSafeHwnd(), 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
}
The GUI in Windows relies on a message loop - somewhere in your code, either explicitly or hidden in a framework, there's a loop that checks for a message in a queue and processes it. If anything blocks the code from returning to that loop, the GUI gets frozen.
There are a few ways around this. One was given by David Brabant, essentially duplicating the loop. Another is to start a new "worker" thread that runs the blocking operation independently. If your message loop has a function that it calls when it is idle, i.e. no more messages are in the queue, you can do some processing there; that's not possible in your example however.
I have a question about threads and controls. I made a syncing modal dialog. There's three progressbars and couple of captions. If application is opened for a first time, then it will open syncing dialog and do the update thing. On dialog Show() method I create three different threads. Each thread controls data download, xml parsing and database inserting. And then shows progress on progressbar. All described, is working fine.
Now the problem - I want to close this dialog automatically when all items are downloaded, parsed and inserted to database. I tried to check if progressbar.position equals to progressbar.max and I tried check if threads are terminated.If I go with the progressbar way, dialog closes too early and one progressbar isn't totally ended. If I go with the thread checking way, then progressbars stop in the middle of process and that's all.
Maybe you have done it and tell the Delphi n00b, how is the best way to do it.
Thanks in advance...
For this simple thing, you can use the thread OnTerminate event (which runs in the context of the main thread) just to decrement a "thread count" variable initialized to 3 at thread creation moment.
When the thread count reaches 0, you can safely close the form.
begin
//..thread creation, stuff
FThreadCount := 3;
DownloadThread.OnTerminate := DecThreadCount;
ParseThread.OnTerminate := DecThreadCount;
InsertThread.OnTerminate := DecThreadCount;
//resume threads and other stuff
end;
procedure TForm1.DecThreadCount(Sender: TObject);
begin
Dec(FThreadCount);
if FThreadCount = 0 then
Close;
end;
Are you using Windows Vista or Windows 7? Microsoft changed the way progress bars work in Vista, so that instead of immediately jumping to the indicated position, it gradually slides towards it. This means that your progress can actually be finished, but the bar won't indicate that for another second or so, so it looks like the dialog is closed before you're done, especially if the bar has a small number of progress steps.
It's kinda ugly, but you can work around this by using a helper function that does something like this:
procedure UpdateProgressBar(bar: TProgressBar);
begin
bar.StepIt;
bar.Max := bar.Max + 1;
bar.Max := bar.Max - 1;
end;
This will ensure that it immediately jumps to the correct position.
EDIT: Details in How do I make TProgressBar stop lagging?
I'd get your threads to post a message back to the dialog when they complete. Once all three messages have been received you can close the dialog.
is it possible to execute a crystal report (TCrpe component) from a Delphi non VCL main thread when Output = toWindow?
I execute reports from a background thread, and when Output is toPrinter or toExport, everything is fine.
I know that creating forms in a
Delphi non VCL main thread generally is a
bad idea.
When a Crystal
Report is executed, and Output=toWindow, the component creates the output
window on its own. So I cannot prevent that the window is created by the background thread.
So is there a clean way to
execute the report in a background
thread, and display the result in a
cleanly created form?
Version: Crystal11VCL7
The following code does not work:
procedure TMyThread.Execute;
var
cr: TCrpe;
begin
inherited;
cr:= TCrpe.Create(nil);
cr.ReportName:= 'C:\TestReport.rpt';
cr.Output:= toWindow;
cr.WindowParent:= Form1; //This is the main form
cr.Execute;
end;
It seems like the report will be created and immediately destroyed afterwards.
When I enter a message loop right after cr.Execute (while true do Application.ProcessMessages; - which is obviously a very bad idea), the report window is shown nicely.
Any idea, how to do it right? Or is it simply not possible? Thanks!
I haven't had any experience with Crystal for many years (thank goodness) but in the absence of any other reply I would consider approaching the problem from a different angle.
What I do is let the report form be created in the main thread - because, as you've said, it's not a good idea to do the VCL stuff in the background - but I generate all report /data/ in a background thread. Once the data is loaded then the thread signals the main form (or whatever) via a windows message and the report goes and connects up to the dataset/datasource.
Initially I display a blank label over the client area of the report window with a message saying something like 'Report loading...' then once the message is received it hides the label and attaches the data. Works really well here and keeps the UI responsive.
Background: I am using OmniThreadLibrary to load batch mode ADO stored procedures in the background. I am doing some slightly dodgy stuff by swapping the connection after opening the SP but that seems to be quite reliable. I'm using PostMessage to send messages back to the calling form and that works in my test applications. Primoz' comms channels work for me, I'm using those for inter-thread comms but for our main application I'm trying to avoid that dependency by using standard PostMessage calls as we do elsewhere in the app.
Problem: Unfortunately when I put this into our main application the PostMessage calls in the thread start failing with 1400:invalid window handle.
I have liberally added extra PostMessage calls and logging code to try to locate the problem, but I'm out of ideas now. The code is boilerplate:
const WM_PW_ADLQUEUEEMPTY = WM_USER + 11;
...
if PostMessage (OwnerHandle, WM_PW_ADLPROGRESS, QueueID, 10) then
pwDebugLog ('TADLQueue.Run WM_PW_ADLPROGRESS send to ' + IntToHex (OwnerHandle, 8) + ' (IsWindow '+BoolToStr(IsWindow(OwnerHandle),true)+') OK for Queue ' + IntToStr (QueueID))
else
pwDebugLog ('TADLQueue.Run WM_PW_ADLPROGRESS send to ' + IntToHex (OwnerHandle, 8) + ' (IsWindow '+BoolToStr(IsWindow(OwnerHandle),true)+') failed for Queue ' + IntToStr (QueueID));
But the log for a series of calls is not very revealing to me. note that the four hex digits after the time is the thread id from GetCurrentThreadID.
15:41:53.221 1614 TpwAsyncDataLoader.RunQueue WM_PW_ADLPROGRESS send to 00A5110C (IsWindow True) OK for Queue -6
15:41:53.265 13B4 TADLQueue.Run WM_PW_ADLPROGRESS send to 00A5110C (IsWindow True) OK for Queue -6
15:41:53.554 13B4 TADLQueueManager.WriteSysErrorMessageToDatabase Postmessage 00A5110C (IsWindow False) failed with 1400 Invalid window handle
Can anyone shed some light on this? I'm confused at how a window handle can become invalid while I'm looking at it, but that's what it looks like to me.
The one thing I can think of is that the form I'm showing here isn't processing messages and I'm seeing a "message queue full" failure rather than the IsWindow(handle) failure that it looks like. How can I test for that?
There are cases where a handle gets recreated, most notably when you change window flags. This might be what's happening in your application.
All I found so far about recreating windows handle is this post from Allen Bauer but I'm certain reading a more detailed one written by Peter Below. Unfortunatly I can't seem to find that one.
Finally, you need to be aware of cases
where your handle may need to get
recreated. This can happen if the
surrounding form or the parent
component's handle goes through a
recreate process. Up until more recent
releases of Windows, the only way to
change some window flags was to
destroy the handle and recreate with
new flags in the CreateWindowEx()
call. There are many components that
still do this. You know if you're in a
recreate situation by checking
(csRecreating in ControlState).
Edit
Not actually the posts from Peter I had in mind but it might give you some fresh ideas.
The form will not have a handle until
you show it the first time (unless
something in the form load sequence
request the handle) but the handle is
not destroyed when you hide the form
and unless you do something that
forces the form to recreate the
handle, e.g. change its border style
or border icons, or call RecreateWnd
yourself the handle will stay the
same.
It may not be desirable but it cannot
be avoided, at least not the way
Delphi drag&dock is currently
implemented. When you dock the dragged
form to another form it becomes a
control (with WS_CHILD window style)
and that means that its window handle
has to be destroyed and recreated with
the new style. And destroying the
window handle of a container control
automatically destroys the handles for
all child controls as well.
and
There is also the fact that the forms
window handle is destroyed and
recreated when you assign to its
Parent property. This also destroys
and recreates the handles for all
controls on the form.
I had a similar issue (but in VC++2010), and I did not find the solution on any forum, so I post it here, hope this will help:
Issue:
Creating a thread,
Passing the HWnd handle
In the thread, PostMessage
throws a 1400 error (invalid handle), although the pointer was equal
with the handle as seen from UI thread (with GetSafeHWnd()).
Solution:
Do not pass the handle, but the parent CDialog(Ex) class
This class has a m_hWnd member that will do the job
Here is a (Cpp) example, sorry for the cast mess.
// In the worker thread
ThreadParam *threadParam = (ThreadParam*)param
// This is ugly because my pointer is a void *, to avoid one more forward declaration
CCoreGenDlg *dlg = static_cast<CCoreGenDlg *>(threadParam->ptr);
// Post
bool b = PostMessage(dlg->m_hWnd ,1221,0,(LPARAM)message);
Cheers'