Reputation:
I'd like to create a progress bar showing the status of a file reading. I read the file using a C++ class Reader that contains a variable _progress.
How can I tell to Cocoa to update the progress bar with the value of reader._progress without writing any ObjC code in the Reader class?
Any help would be appreciated.
ProgressController *pc = [[ProgressController alloc] init];
[pc showWindow:sender];
// Create the block that we wish to run on a different thread
void (^progressBlock)(void);
progressBlock = ^{
[pc.pi setDoubleValue:0.0];
[pc.pi startAnimation:sender];
Reader reader("/path/to/myfile.txt");
reader.read();
while (reader._progress < 100.)
{
dispatch_async(dispatch_get_main_queue(), ^{
[pc.pi setDoubleValue:reader._progress];
[pc.pi setNeedsDisplay:YES];
});
}
}; // end of progressBlock
// Finally, run the block on a different thread
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, progressBlock);
So here is my second try.
The reader code:
class PDBReader
{
public:
Reader(const char *filename);
Reader(string filename);
~Reader();
int read();
string _filename;
float _progress;
void setCallback(void (^cb)(double))
{
if (_cb)
{
Block_release(_cb);
_cb = Block_copy(cb);
}
}
void (^_cb)(double);
protected:
private:
};
int Reader::read()
{
string buffer;
unsigned atomid = 0;
ifstream file;
file.open(_filename.c_str(), ifstream::in);
if (!file.is_open())
{
return IOERROR;
}
file.seekg(0, ios_base::end);
float eof = (float) file.tellg();
file.seekg(0, ios_base::beg);
while (getline(file, buffer))
{
_progress = (float) file.tellg() / eof * 100.;
if (_cb)
{
_cb(_progress);
}
// some more parsing here...
}
file.close();
return SUCCESS;
}
PDBReader::~PDBReader()
{
if (_cb)
{
Block_release(_cb);
}
}
And the Cocoa part:
-(IBAction) test:(id) sender
{
ProgressController *pc = [[ProgressController alloc] init];
[pc showWindow:sender];
Reader reader("test.txt");
reader.setCallback(^(double progress)
{
dispatch_async(dispatch_get_main_queue(), ^{
[pc.pi setDoubleValue:progress];
[pc.pi setNeedsDisplay:YES];
});
});
reader.read();
}
Thanks for your help.
Upvotes: 2
Views: 862
Reputation:
Finally!!!!!
It work's well when do not put it into a queue.
ProgressController *pc = [[ProgressController alloc] init];
[pc showWindow:sender];
[pc.pi setUsesThreadedAnimation:YES];
Reader reader("test.txt");
reader.setCallback(^(double progress)
{
[pc.pi setDoubleValue:progress];
[pc.pi setNeedsDisplay:YES];
});
reader.read();
But why do you say "This is bad, though, because it blocks the main thread"? Because basically my program has to wait the file to be read before doing anything else. Is there some basic optimization I miss here?
Thanks very much for your help.
Upvotes: 0
Reputation: 90651
Just because you don't want to make Reader include Objective-C code doesn't mean you can only observe it from the outside. It can call a C function via a passed-in function pointer. It can use a more general functor (function object) mechanism. It can even take a block.
You definitely don't want to do that while (reader._progress < 100.)
loop. That's a busy loop. It will spin around updating the progress as fast as computerly possible. It will peg a CPU core at 100% utilization. In fact, it will probably queue tasks to the main dispatch queue faster than they can be run.
You only want to update the progress indicator when the Reader has updated its _progress
member, which will require some sort of cooperation from the Reader class.
Upvotes: 7