Re: Progress Indicators Spin vs Bar
Re: Progress Indicators Spin vs Bar
- Subject: Re: Progress Indicators Spin vs Bar
- From: Ken Tozier <email@hidden>
- Date: Tue, 21 Jun 2011 00:03:42 -0400
Hi James,
I recently finished a file routing app that does a lot of heavy duty operations to files and found that the combination of NSInvocationOperations/NSOperationQueue to be really programmer friendly.
Among the stuff my app does is copy large image files from A to B, creates thumbnails and grayscale versions of them and writes the info to a database and with NSOperationQueues, there is basically no slowdown at all in the user interface. Queues can be stopped at any time so you could give the user that option. Here's a stripped down skeleton of how I'm using NSOperationQueue/NSInvocationOperation which might get you up and running with queues a bit faster. The key is, they are really forgiving and simple to use.
Of particular interest to you might be the delegate idea which allows the file processing method to continuously update and send info to another process which would help with your progress indicator issue
I just did a test with 1258 full sized images and it routed them, created thumbnails, grayscales and extracted a bunch of info in 3 minutes 24 seconds with no effect on user responsiveness of the machine or the app.
Enjoy
// In this example, your delegate in the user interface code, might have a handler defined like so
- (void) handleImageInfo:(id) inMessage
{
if ([inMessage isKindOfClass: [NSNumber class]])
// do something with the file count
else if ([inMessage isKindOfClass: [NSDictionary class]])
// add data from inMessage to your user interface
}
// Class that monitors and processes files in a drop folder
// For you, it might be a user selected scanning folder
- (id) init
{
self = [super init];
if (self)
{
queue = [[NSOperationQueue alloc] init];
delegate = nil;
selector = nil;
return self;
}
[self release];
return nil;
}
- (void) setDelegate:(id) inDelegate
selector:(SEL) inSelector
{
delegate = [inDelegate retain];
selector = inSelector;
}
- (void) start
{
[self queueOperation];
}
- (void) stop
{
[queue setSuspended: YES];
}
- (void) queueOperation
{
NSInvocationOperation *operation;
operation = [[[NSInvocationOperation alloc]
initWithTarget: self
selector:@selector(processDropFolder)
object: nil]
autorelease];
[operation addObserver: self
forKeyPath: @"isFinished"
options: NSKeyValueObservingOptionNew
context: NULL];
[queue addOperation: operation];
}
- (void) processDropFolder
{
NSArray *fileNames = UtilContentsOfDirectoryAtPath(dropFolder);
if ((fileNames != nil) && ([fileNames count] > 0))
{
NSNumber *fileCount = [NSNumber numberWithInt: [fileNames count]];
// send file count off to delegate
[delegate performSelector: selector withObject: fileCount];
// we have some photos to process
NSEnumerator *e = [fileNames objectEnumerator];
NSString *filePath;
NSMutableDictionary *fileInfo;
NSLog(@"Scanning: %@", dropFolder);
while (fileName = [e nextObject])
{
// ignore invisible files
if (![fileName hasPrefix: @"."])
{
filePath = [dropFolder stringByAppendingPathComponent: fileName];
// read the exif info, find (or create) a thumbnail
// package it up in a nice NSDictionary and send it
// to your delegate
NSDictionary *imgInfo = UtilImageInfo(filePath);
if (imgInfo != nil)
[delegate performSelector: selector withObject: imgInfo];
}
}
}
}
- (void)observeValueForKeyPath:(NSString *) inKeyPath
ofObject:(id) inObject
change:(NSDictionary *) inChange
context:(void *) inContext
{
if ([inKeyPath isEqualToString: @"isFinished"])
{
// remove invocation observer
[inObject removeObserver: self forKeyPath: @"isFinished"];
// queue up another pass
[self queueOperation];
}
}
// utility function that reads info from an image file
NSMutableDictionary *UtilImageInfo(NSString *inPath)
{
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
NSError *error = nil;
NSString *type = [workspace typeOfFile: inPath error: &error];
if ((error == nil) && [workspace type: type conformsToType: (NSString *) kUTTypeImage])
{
// inPath is some sort of image
NSURL *url = [NSURL fileURLWithPath: inPath];
CGImageSourceRef imgSrc = CGImageSourceCreateWithURL((CFURLRef) url, NULL);
if (imgSrc != nil)
{
NSDictionary *props = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(imgSrc,0, NULL);
NSMutableDictionary *result = [NSMutableDictionary dictionary];
// copy out whatever info you're interested from props
NSNumber *zero = [NSNumber numberWithInt: 0];
NSString *colorModel = [props objectForKey: @"ColorModel"],
*colorProfile = [props objectForKey: @"ProfileName"],
*pixelWidth = [props objectForKey: @"PixelWidth"],
*pixelHeight = [props objectForKey: @"PixelHeight"],
*bitsPerPixel = [props objectForKey: @"Depth"];
[result setObject: ((colorModel == nil) ? @"" : colorModel) forKey: @"color"];
[result setObject: ((colorProfile == nil) ? @"" : colorProfile) forKey: @"profile"];
[result setObject: ((pixelWidth == nil) ? zero : [NSNumber numberWithFloat:[pixelWidth floatValue]]) forKey: @"width"];
[result setObject: ((pixelHeight == nil) ? zero : [NSNumber numberWithFloat:[pixelHeight floatValue]]) forKey: @"height"];
[result setObject: ((bitsPerPixel == nil) ? zero : [NSNumber numberWithFloat:[bitsPerPixel floatValue]]) forKey: @"bits_per_pixel"];
CFRelease(imgSrc);
[props release];
return result;
}
}
return nil;
}
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden