Refresh window in loop - xcode

I have an application written in xcode/cocoa on Mac.
A label on the main window is changed in every occurrence of a heavy loop with [label setStringValue], however it is refreshed only at the end of the loop.
How can I have it refreshed in each occurrence ?
Thanks !

You should use a queue. Your heavy loop in backgroundQueue and [label setStringValue] in mainQueue.
Example:
dispatch_queue_t backgroundQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(backgroundQueue,^{
//Your loop
dispatch_async(mainQueue,^{
//Set Label value
});
});

Your problem is that you do the work (the loop) on the main thread. The main thread is responsible for updating the UI and must not be blocked!
You need to start a new thread to do the heavy work and update your UI on the main thread.
You should have a look at GCD which is a good an lightweight solution for that or have a look at the performSelector... methods.

Related

DrawRect and NSProgressIndicator

I will be very short:
I'am drawing lines using drawrect and NSBezierPath.
To draw these lines I am using a for loop.
Now, since the loop takes many seconds,I'm trying to use a NSProgresIndicator to show the progress and to update it within the loop I used dispatch_async & queue.. The ProgressIndicator updates but nothing is being drawn.. If don't use the queue the lines are drawn but the indicator updates at the end of the cycle.
What am I doing wrong ?
The error:
<Error>: CGContextRestoreGState: invalid context 0x0.
This is a serious error.
This application, or a library it uses, is using an invalid context
and is thereby
contributing to an overall degradation of system stability and reliability.
This notice is a courtesy: please fix this problem.
It will become a fatal error in an upcoming update.
// SAMPLE OF CODE
dispatch_queue_t myQueue = dispatch_queue_create("my queue", NULL);
dispatch_async(myQueue, ^
{
for (int i = 1; i <= 200; i++) {
dispatch_async(dispatch_get_main_queue(), ^{
[istance set_progress:i];
//This sets the progress, but bezierPath doesn't work
});
//Bezier already alloc & init
[my_path moveToPoint....]
[my_path lineToPoint....]
[my_path stroke]
}
});
In general you shouldn't be drawing on a background thread or queue.
Please read this document for information about multithreaded drawing. Skip to "Drawing Restrictions"
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html

Performing an action ONCE after a delay of n seconds, WP7 C#

I'm looking for a simple way to perform an action/method after a delay of n seconds. The thing I found a few examples but they seem overly complex for when on my last platform, iOS, it was just
[self performSelector:#selector(methodname) withDelay:3];
Any tips or code snippets would be much appreciated.
You can also use Scheduler.Dispatcher from Microsoft.Phone.Reactive:
Scheduler.Dispatcher.Schedule(MethodName, TimeSpan.FromSeconds(5));
private void MethodName()
{
// This happens 5 seconds later (on the UI thread)
}
DispatcherTimer DelayedTimer = new DispatcherTimer()
{
Interval = TimeSpan.FromSeconds(5)
};
DelayedTimer.Tick += (s, e) =>
{
//perform action
DelayedTimer.Stop();
}
DelayedTimer.Start();
For Windows Phone 8 you can use
await Task.Delay(milliseconds);
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += (s, e) =>
{
// do some very quick work here
// update the UI
StatusText.Text = DateTime.Now.Second.ToString();
};
timer.Interval = TimeSpan.FromSeconds(1);
timer.Start();
Note that what you're doing here is interrupting the UI thread, not really running anything on a separate thread. It's not suitable for anything long-running and cpu-intensive, but rather something where you need to execute on a regular interval. Clock UI updates are a perfect example.
Also Timers are not guaranteed to execute exactly when the time interval occurs, but they are guaranteed to not execute before the time interval occurs. This is because DispatcherTimer operations are placed on the Dispatcher queue like other operations. When the DispatcherTimer operation executes is dependent on the other jobs in the queue and their priorities.
For more information use this link
If you want to use Timer for the background task then use the
System.Threading.Timer instead of DispatcherTimer
For more information use this link

NSProgressIndicator will not disappear (determinate)

I have a determinate progress indicator. It is working just like I would expect it to but it does not disappear after it reaches maxValue. I have set the progress indicator to not display when stopped in the main.nib file, I have also entered it into the awakeFromNib{} method.
I put a log at the end of the routine to make sure the [displayWhenStopped] setting was still set to NO and it is.
Here is my code :
-(void)getEvents:(NSURL *)mffFile{
NSMutableArray * eventTypeResults =[[NSMutableArray alloc]init];
EWaveformRecording *ptrToRcdFile = [EWaveformRecording openRecording:mffFile permission:readOnly user:nil convertFileFormat:NO openReadOnlyAnyway:NO];
NSArray *events = [ptrToRcdFile getEvents];
//get the size of events for the progressbar and instantiate a loop counter
NSInteger total = [events count];
int loop = 0;
//set progress bar params
[self->meter_ setUsesThreadedAnimation:YES];
[self->meter_ setControlSize:NSMiniControlSize];
[self->meter_ setMaxValue:(double)total];
for(EEvent* event in events){
loop ++;
if(![eventTypeResults containsObject:event.code]){
NSLog(#"check eventNames in getEvents %#", event.code);
[eventTypeResults addObject: event.code];
}//end if
//display loop increment in progress bar
[self->meter_ setDoubleValue:(1000*(double)loop)/1000];
}//end for
//send the eventTypesResults array to the EventExport class
[evtPtr setEventsAvailableList:eventTypeResults];
}
What I have tried:
with and without [setUsesThreadedAnimation] which I don't totally understand; it does slow down the progress bar which makes it look better but the docs say only indeterminate types should be effected by animation.
I have tried using [start & stop animation]
I have tried [setDisplayWhenStopped:NO] after my loop
Any help is greatly appreciated
MIke
This is what I learned.
I should not be allowing the progress bar to run on a different thread even though it looks like its working because the NSProgressIndicator can no longer respond to the settings in the main thread, so the proper thing to do is to not instantiate that method, however , that was not the solution to my problem; I was doing everything else right, but the main thread could not redraw the progress because it's busy with all the other calls in the UI. The solution was to implement NSRunLoop so that each iteration of the loop interrupts the main thread and redraws the progress meter , then returns control.

my PROGRESS BAR redraws not all, WINAPI

in my application I am entering a loop of unknown number of elements,
so I am showing a progress bar to inform that application is running,
and this code works FINE, progres bar is refreshing independly:
do
{
...
SendMessage(hPBLoading, PBM_STEPIT, 0, 0);
...
} while(true);
but unfortunately the rest of window doesn't refresh (which is obvious due to the loop)
and in Windows 7 after a few seconds Windows treat my app like it break down, and it back and refresh after ending the loop,
so I figured out that I need to dispatch the message queue and I changed my code to this:
do
{
...
SendMessage(hPBLoading, PBM_STEPIT, 0, 0);
...
us_tmpBreakCounter++;
...
if (us_tmpBreakCounter >= 10)
{
us_tmpBreakCounter = 0;
UpdateSomeOtherElementsinWindow();
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
} while(true);
It works fine, the applcation is updated and refreshed after ex. 10 counts so its never break down,
but unfortunately another problem occured, now the PROGRESS BAR doesn't redraw fully - the beveled frame dissapears after a few seconds, check this image below:
http://mstanisz.website.pl/temp/progressbar_01.jpg
Thanks For Your Help!
m.
Seems like you're using a GUI thread to do some long task. That's not a good practice. When you want to perform long tasks, use a worker thread for that, and let the GUI thread handle the GUI while the worker works. You'll have to do some extra work taking care of synchronization between the two threads (stop the worker if the GUI is closed, avoid starting a task before the current one has ended and so on), but that's the way it works.

threaded NSProgressIndicator problem

I'm trying to figure out how to update an indeterminate NSProgressIndicator in the UI using a secondary thread while the main thread does some heavy lifting, just like dozens of apps do.This snippet is based on Apple's "Trivial Threads" example using Distributed Objects (DO's):
// In the main (client) thread...
- (void)doSomethingSlow:(id)sender
{
[transferServer showProgress:self];
int ctr;
for (ctr=0; ctr <= 100; ctr++)
{
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
NSLog(#"running long task...");
}
}
// In the secondary (server) thread...
- (oneway void)showProgress:(Controller*)controller
{
[controller resetProgressBar];
float ticks;
for (ticks=0; ticks <= 100; ticks++)
{
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[controller updateProgress:ticks];
NSLog(#"updating progress in UI...");
}
}
Unfortunately however, there's no way I can get both threads to run concurrently. Either the secondary thread will run and the main thread waits until it's finished OR the main thread runs followed by the secondary thread -- but not both at the same time.
Even if I pass a pointer to the server thread and ask it to update the progress bar directly (without calling the main thread back) it makes no difference. It seems that once the main thread enters a loop like this it ignores all objects sent to it. I'm still a novice with Obj-C and I'd really appreciate any help with this.
AppKit isn't thread-safe, at all. You have to update the UI from the main thread, or all sorts of crazy stuff will happen (or it just won't work).
The best way is to do your work on the secondary thread, calling back to the main thread when you need to update the UI:
-(void)doSomethingSlow:(id)sender {
[NSThread detachNewThreadSelector:#selector(threadedMethod) toTarget:self withObject:nil];
// This will return immediately.
}
-(void)threadedMethod {
int ctr;
for (ctr=0; ctr <= 100; ctr++) {
NSLog(#"running long task...");
[self performSelectorOnMainThread:#selector(updateUI)];
}
}
-(void)updateUI {
// This will be called on the main thread, and update the controls properly.
[controller resetProgressBar];
}
You might want to try switching your threads. In general, UI updates and user input are handled on the main thread, and heavy tasks are left for secondary threads.

Resources