while loop with sleep(): logging works, but not UI events

2008-06-27 Thread Daniel Richman

Hi All,

I'm trying to program a simple timer app: you enter a number of seconds, 
and it updates a text field every second with the number of secs 
remaining. The problem is that I'm not able to do anything with the UI 
while this is going on. Here's my code:


- (IBAction)startTimer:(id)sender
{
   [startButton setState:NSOffState];
  
   int timeInSeconds = [((NSNumber *)[inTextField objectValue]) intValue];
  
   while (timeInSeconds > 0) {
   [outTextField setStringValue:[NSString stringWithFormat:@"%d 
more second(s) to go", timeInSeconds]];
  
   sleep(1);

   timeInSeconds--;
   }
  
   [outTextField setStringValue:@"Finished timing"];

   NSBeep();
}

What's happening is that if I enter a number and then press 'start', 
'start' stays in the 'pressed' position until the loop finishes, after 
which it returns to the normal position. My guess is that this is 
stopping any other UI events from occurring. This would also explain why 
I'm able to log a message each time the loop iterates.


But I'm still not sure why the button is staying pressed. Thoughts, anyone?

Daniel
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]


Re: while loop with sleep(): logging works, but not UI events

2008-06-27 Thread Shawn Erickson
On Fri, Jun 27, 2008 at 8:55 PM, Daniel Richman
<[EMAIL PROTECTED]> wrote:
> Hi All,
>
> I'm trying to program a simple timer app: you enter a number of seconds, and
> it updates a text field every second with the number of secs remaining. The
> problem is that I'm not able to do anything with the UI while this is going
> on. Here's my code:

Don't loop like this. Think about what is taking place... you are
looping never allowing your main thread to do anything else. Your loop
is not returning out to the normal runloop which handles user input
and drives your UI.

Look at using NSTimer to trigger a callback that will allow you to
update UI as needed.

-Shawn
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]


Re: while loop with sleep(): logging works, but not UI events

2008-06-27 Thread Stephen J. Butler
On Fri, Jun 27, 2008 at 10:55 PM, Daniel Richman
<[EMAIL PROTECTED]> wrote:
> What's happening is that if I enter a number and then press 'start', 'start'
> stays in the 'pressed' position until the loop finishes, after which it
> returns to the normal position. My guess is that this is stopping any other
> UI events from occurring. This would also explain why I'm able to log a
> message each time the loop iterates.
>
> But I'm still not sure why the button is staying pressed. Thoughts, anyone?

If you're not running the event loop then the button has no chance to
repaint. You absolutely cannot block the main thread like this and
expect the GUI to still function.

Any reason you're not using NSTimer? Along with the painting issues,
this strategy also won't work 100% of the time because sleep() is only
guaranteed to block for *at most* a certain number of seconds. It can
exit early if it chooses to, in particular it will if you don't mask
signals from being delivered on your thread,
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]


Re: while loop with sleep(): logging works, but not UI events

2008-06-27 Thread Shawn Erickson
On Fri, Jun 27, 2008 at 8:59 PM, Shawn Erickson <[EMAIL PROTECTED]> wrote:
> On Fri, Jun 27, 2008 at 8:55 PM, Daniel Richman
> <[EMAIL PROTECTED]> wrote:
>> Hi All,
>>
>> I'm trying to program a simple timer app: you enter a number of seconds, and
>> it updates a text field every second with the number of secs remaining. The
>> problem is that I'm not able to do anything with the UI while this is going
>> on. Here's my code:
>
> Look at using NSTimer to trigger a callback that will allow you to
> update UI as needed.

As Stephen noted... you need record the start time (or record the end
time) then check current time (look at NSDate) to ensure you count out
the requested amount of time. Also you should fire your timer every
0.75 of seconds (or so) to ensure your UI update is consistent/smooth.
Firing every second may cause your timer to fire a little after 1
second later so your UI could skip from say 50s to 52s.

-Shawn
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]


Re: while loop with sleep(): logging works, but not UI events

2008-06-28 Thread Michael Ash
On Sat, Jun 28, 2008 at 12:10 AM, Shawn Erickson <[EMAIL PROTECTED]> wrote:
> Also you should fire your timer every
> 0.75 of seconds (or so) to ensure your UI update is consistent/smooth.
> Firing every second may cause your timer to fire a little after 1
> second later so your UI could skip from say 50s to 52s.

I do not recommend this, as using 0.75s will guarantee a jittery
update. Consider what happens when you start at 0 and round down to
figure out what to display. You'll display 0, then 0 again. Then
you'll display 1, at 1.5 seconds after start, then 2 and 3 each 0.75
seconds after the previous number. The cycle repeats with 4, which
displays 1.5 seconds after 3.

Firing once a second is usually just fine, so if you want to keep
things simple, do that. To ensure against skipping, round to the
nearest integer, which will ensure that you can be off by up to .5
seconds without skipping any numbers.

Mike
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]


Re: while loop with sleep(): logging works, but not UI events

2008-06-28 Thread Shawn Erickson
On Sat, Jun 28, 2008 at 7:58 AM, Michael Ash <[EMAIL PROTECTED]> wrote:
> On Sat, Jun 28, 2008 at 12:10 AM, Shawn Erickson <[EMAIL PROTECTED]> wrote:
>> Also you should fire your timer every
>> 0.75 of seconds (or so) to ensure your UI update is consistent/smooth.
>> Firing every second may cause your timer to fire a little after 1
>> second later so your UI could skip from say 50s to 52s.
>
> I do not recommend this, as using 0.75s will guarantee a jittery
> update. Consider what happens when you start at 0 and round down to
> figure out what to display. You'll display 0, then 0 again. Then
> you'll display 1, at 1.5 seconds after start, then 2 and 3 each 0.75
> seconds after the previous number. The cycle repeats with 4, which
> displays 1.5 seconds after 3.

Actually I meant to type 0.5s which is what I use in my code for this
type of thing and you don't want to use the timer to track time but
the actual system clock... assuming you use a simple repeating timer.

Firing once every 1 second can cause you skip numbers as timer jitter
walks near to a second boundary based on wall clock time assuming
you use a simple repeating timer and don't round as you suggest.

If you reschedule a timer every time the prior timer fires you can use
a 1 second timer delay if you attempt to ensure that your current time
isn't close to a second boundary of the wall clock.

..or rounding can be used (depends on how you are calculating the time, etc.)

-Shawn
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]


Re: while loop with sleep(): logging works, but not UI events

2008-06-28 Thread Daniel Richman
Thanks very much for this detailed explanation. I realize my mistake 
now; I was thinking about this in the wrong way. I eventually coded it 
as follows:


- (IBAction)startTimer:(id)sender
{   
   timeInSeconds = [((NSNumber *)[inTextField objectValue]) intValue];

   [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
  selector:@selector(checkTimer:)
  userInfo:nil
   repeats:YES];
}


- (void)checkTimer:(NSTimer *)timer
{
   if (timeInSeconds == 0) {
   [timer invalidate];
   [outTextField setStringValue:@"Finished timing"];
   } else {
   [outTextField setStringValue:[NSString stringWithFormat:@"%d 
more second(s) to go", timeInSeconds]];

   NSLog(@"in loop, timeInSeconds is %d", timeInSeconds);
   timeInSeconds--;
   }
}   



Thanks again to all.
Daniel


Ken Thomases wrote:

On Jun 27, 2008, at 10:55 PM, Daniel Richman wrote:

I'm trying to program a simple timer app: you enter a number of 
seconds, and it updates a text field every second with the number of 
secs remaining. The problem is that I'm not able to do anything with 
the UI while this is going on. Here's my code:


- (IBAction)startTimer:(id)sender
{
  [startButton setState:NSOffState];
int timeInSeconds = [((NSNumber *)[inTextField objectValue]) 
intValue];

while (timeInSeconds > 0) {
  [outTextField setStringValue:[NSString stringWithFormat:@"%d 
more second(s) to go", timeInSeconds]];

sleep(1);
  timeInSeconds--;
  }
[outTextField setStringValue:@"Finished timing"];
  NSBeep();
}

What's happening is that if I enter a number and then press 'start', 
'start' stays in the 'pressed' position until the loop finishes, 
after which it returns to the normal position. My guess is that this 
is stopping any other UI events from occurring. This would also 
explain why I'm able to log a message each time the loop iterates.


But I'm still not sure why the button is staying pressed. Thoughts, 
anyone?


What others have said is correct, but I wanted to provide some more 
explanation.


Let's think about why the button is staying pressed.  I'll illustrate 
purely using pseudocode.  I'm _not_ implying that this is literally 
what's happening, but let's imagine that your button click is 
processed by code which looks something like this:


while (get an event)
{
if (event is a mouse click on a button)
{
paint the button as pressed
invoke the button's action method on the button's target  // 
this calls your startTimer: method

paint the button as normal
}
else /* ... */
}

Now, can you see why the button is staying pressed?  The invocation of 
your startTimer: method is not exiting until N seconds go by, and the 
button doesn't get repainted until after it does.  The code in the 
framework isn't magical or special.  Just like code that you write, it 
has to finish with one thing before it can go on to the next thing.  
In your code, you wouldn't expect the [outTextField 
setStringValue:@"Finished timing"] to be performed before the while 
loop exits.  Why would you expect anything else to?


Next, let's image we insert some code into the while loop, at the end 
of the block:


while (get an event)
{
if (event is a mouse click on a button)
/* ... same as above ... */

for each window
{
for each view in this window which needs to be redrawn due to 
a state change

draw this view
}
}

This window and view redrawing code is what is responsible for showing 
the changes you make to the output text field.  So, by blocking inside 
startTimer: you're not only preventing the button from being redrawn, 
you're also preventing the output text field from being redrawn with 
its new content.



That's the explanation behind what the others have told you.  Unless 
and until your method returns, none of the other work that's waiting 
to be done will get done.


I hope that helps.

Cheers,
Ken

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]


Re: while loop with sleep(): logging works, but not UI events

2008-06-28 Thread Daniel Richman
One second seems to be accurate enough for my purposes, but I'll 
investigate using 0.5 seconds.


Daniel


Shawn Erickson wrote:

On Sat, Jun 28, 2008 at 7:58 AM, Michael Ash <[EMAIL PROTECTED]> wrote:
  

On Sat, Jun 28, 2008 at 12:10 AM, Shawn Erickson <[EMAIL PROTECTED]> wrote:


Also you should fire your timer every
0.75 of seconds (or so) to ensure your UI update is consistent/smooth.
Firing every second may cause your timer to fire a little after 1
second later so your UI could skip from say 50s to 52s.
  

I do not recommend this, as using 0.75s will guarantee a jittery
update. Consider what happens when you start at 0 and round down to
figure out what to display. You'll display 0, then 0 again. Then
you'll display 1, at 1.5 seconds after start, then 2 and 3 each 0.75
seconds after the previous number. The cycle repeats with 4, which
displays 1.5 seconds after 3.



Actually I meant to type 0.5s which is what I use in my code for this
type of thing and you don't want to use the timer to track time but
the actual system clock... assuming you use a simple repeating timer.

Firing once every 1 second can cause you skip numbers as timer jitter
walks near to a second boundary based on wall clock time assuming
you use a simple repeating timer and don't round as you suggest.

If you reschedule a timer every time the prior timer fires you can use
a 1 second timer delay if you attempt to ensure that your current time
isn't close to a second boundary of the wall clock.

..or rounding can be used (depends on how you are calculating the time, etc.)

-Shawn
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/applemaillist%40mm.danielrichman.com

This email sent to [EMAIL PROTECTED]
  

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]


Re: while loop with sleep(): logging works, but not UI events

2008-06-28 Thread Shawn Erickson
On Sat, Jun 28, 2008 at 8:29 AM, Shawn Erickson <[EMAIL PROTECTED]> wrote:
> On Sat, Jun 28, 2008 at 8:13 AM, Daniel Richman
> <[EMAIL PROTECTED]> wrote:
>> Thanks very much for this detailed explanation. I realize my mistake now; I
>> was thinking about this in the wrong way. I eventually coded it as follows:
>>
>> - (IBAction)startTimer:(id)sender
>> { timeInSeconds = [((NSNumber *)[inTextField objectValue]) intValue];
>>   [NSTimer scheduledTimerWithTimeInterval:1.0
>>target:self
>>  selector:@selector(checkTimer:)
>>  userInfo:nil
>>   repeats:YES];
>> }
>>
>>
>> - (void)checkTimer:(NSTimer *)timer
>> {
>>   if (timeInSeconds == 0) {
>>   [timer invalidate];
>>   [outTextField setStringValue:@"Finished timing"];
>>   } else {
>>   [outTextField setStringValue:[NSString stringWithFormat:@"%d more
>> second(s) to go", timeInSeconds]];
>>   NSLog(@"in loop, timeInSeconds is %d", timeInSeconds);
>>   timeInSeconds--;
>>   }
>> }
>
> You really shouldn't use an NSTimer to count time as you are doing
> above. For example your timer can be delayed from firing because of
> other work taking place on the main thread. Over a long enough period
> of time (use enters a larger value for seconds) these small delays can
> build up and cause you to miscount time when compared to wall clock
> time.
>
> You should instead use something like NSDate. For example create an
> NSDate instance for the finish time (consider +[NSDate
> dateWithTimeIntervalSinceNow:]) in you action method and then in your
> check timer method look at that date to see if you have passed it to
> know if you are done. Also you would use current time and finish time
> to calculate how many seconds remand. You get current time using
> -[NSDate date].
>
> You could also consider two timers... one firing often to cause you to
> update the UI and the other firing once at the finish time.

Ah found it...

http://lists.apple.com/archives/cocoa-dev/2006/Nov/msg00447.html

...and you can count using a timer if you use a constant time based
(see my email related to the linked email thread)...

http://lists.apple.com/archives/cocoa-dev/2006/Nov/msg00494.html

-Shawn
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]