| View previous topic :: View next topic |
| Author |
Message |
DAVID B MORGAN Guest
|
Posted: Sun Jan 15, 2006 10:03 am Post subject: Just sharing a coding trick |
|
|
Hi everybody,
This technique may be well known to some of you, but it is
new to me. It may also be bad practice; I don't know!
I had a code problem recently. I realize that I could
probably have solved it with rethinking the logical flow of the
program, but it was easier and faster doing it this way.
=============The Scenario=============
1. When enabled a Ttimer would decrement a scrollbar
one position each time it fires.
2. The user could also manually adjust the scrollbar at any time.
3. I needed the scrollbar.onchange event to fire only when the
scrollbar position was changed manually NOT when its
position was changed by the Ttimer.
4. Scrollbar.onchange event needs to fire different parts of the
Ttimer event depending on if it is enabled or disabled
=============The Problem===============
Ttimer fired scrollbar.onchange that fired TTimer that fired
scrollbar.onchange etc...[Infinite Loop]
=============The Solution===============
procedure TForm1.Timer1Timer(Sender: TObject);
begin
If timer1.enabled=true then
begin
//Lots of code here
end;
scrollbar1.OnChange:=nil; {<- here is the trick}
scrollbar1.Position:=scrollbar1.Position-1;
scrollbar1.OnChange:=ScrollBar1Change;
if timer1.enabled =false then
begin
// Lots of code here
end;
end;
procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
//Lots of code here
timer1timer(sender);
//Lots of code here
end;
|
|
| Back to top |
|
 |
alanglloyd@aol.com Guest
|
Posted: Sun Jan 15, 2006 12:45 pm Post subject: Re: Just sharing a coding trick |
|
|
When you want the timer to move the scrollbar set its Enabled to true.
In TScrollBar.OnScroll event handler (called only when the user moves
the scrolbar) set TTimer.Enabled to false.
Your "trick" is a valid one within an event handler to prevent
recursive operation. Its not often used elsewhere, and here its better
IMO to use OnScroll.
Your ...
if timer1.enabled =false then
begin
// Lots of code here
end;
.... will be executed when you call Timer1Timer but IMO its better for
this sort of action to call an event handler with a nil parameter and
test for that. Its clearer code too. Both in the event handler and the
calling code it is then obvious that the timer is not calling it. It is
slightly confusing to code an "if control is not enabled" in an event
handler which one usually expects to be called only when the control is
enabled. That's why I think nil testing is better and obviously
indicates a non-control call of the event handler.
Alan Lloyd
|
|
| Back to top |
|
 |
DAVID B MORGAN Guest
|
Posted: Sun Jan 15, 2006 12:49 pm Post subject: Re: Just sharing a coding trick |
|
|
Alan,
Thank you for your feedback.
David
|
|
| Back to top |
|
 |
Tom de Neef Guest
|
Posted: Sun Jan 15, 2006 5:13 pm Post subject: Re: Just sharing a coding trick |
|
|
"DAVID B MORGAN" <lepton2 (AT) verizon (DOT) net> schreef in bericht
news:J5pyf.863$FS3.675 (AT) trndny04 (DOT) ..
| Quote: |
procedure TForm1.Timer1Timer(Sender: TObject);
begin
If timer1.enabled=true then
begin
//Lots of code here
end;
scrollbar1.OnChange:=nil; {<- here is the trick}
scrollbar1.Position:=scrollbar1.Position-1;
scrollbar1.OnChange:=ScrollBar1Change;
if timer1.enabled =false then
begin
// Lots of code here
end;
end;
procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
//Lots of code here
timer1timer(sender);
//Lots of code here
end;
|
It's a bit like enabling and disabling application interrupts, as in:
procedure disableInterrupts;
// avoid interrupts that can corrupt the calculation process currently
running
begin
Application.OnMessage:=MainForm.AppMessage;
end;
procedure enableInterrupts;
// allow interrupts
begin
Application.OnMessage:=NIL;
end;
procedure TMainForm.AppMessage(var Msg: TMsg; var Handled: Boolean);
// trap all user actions and dump them except an ESC key press
begin
if (Msg.wParam<>27) AND hintWindow.IsHintMsg(Msg)
then Handled := True;
end;
Two remarks - no wish to be pedantic - about the code itself:
1) use "If timer1.enabled" and "If NOT timer1.enabled" instead of "If
timer1.enabled=true" and "If timer1.enabled=false" (the results may be
different on different compilers).
2) save the old value of the handler, as in
oldOnChange:=scrollbar1.OnChange;
scrollbar1.OnChange:=nil; {<- here is the trick}
scrollbar1.Position:=scrollbar1.Position-1;
scrollbar1.OnChange:=oldOnChange;
Tom
|
|
| Back to top |
|
 |
Jamie Guest
|
Posted: Sun Jan 15, 2006 6:09 pm Post subject: Re: Just sharing a coding trick |
|
|
DAVID B MORGAN wrote:
| Quote: | Hi everybody,
This technique may be well known to some of you, but it is
new to me. It may also be bad practice; I don't know!
I had a code problem recently. I realize that I could
probably have solved it with rethinking the logical flow of the
program, but it was easier and faster doing it this way.
=============The Scenario=============
1. When enabled a Ttimer would decrement a scrollbar
one position each time it fires.
2. The user could also manually adjust the scrollbar at any time.
3. I needed the scrollbar.onchange event to fire only when the
scrollbar position was changed manually NOT when its
position was changed by the Ttimer.
4. Scrollbar.onchange event needs to fire different parts of the
Ttimer event depending on if it is enabled or disabled
=============The Problem===============
Ttimer fired scrollbar.onchange that fired TTimer that fired
scrollbar.onchange etc...[Infinite Loop]
=============The Solution===============
procedure TForm1.Timer1Timer(Sender: TObject);
begin
If timer1.enabled=true then
begin
//Lots of code here
end;
scrollbar1.OnChange:=nil; {<- here is the trick}
scrollbar1.Position:=scrollbar1.Position-1;
scrollbar1.OnChange:=ScrollBar1Change;
if timer1.enabled =false then
begin
// Lots of code here
end;
end;
procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
//Lots of code here
timer1timer(sender);
//Lots of code here
end;
here is a trick i use to prevent re'entry of the code block. |
procedure Tform1.ScrollBar1.Change(Sender:TObject);
Const Busy :Boolean = false; // make sure write able consts are on..
Begin
If Busy then exit else busy := true;
// do your code...
Busy := false;
End;
--
Real Programmers Do things like this.
http://webpages.charter.net/jamie_5
|
|
| Back to top |
|
 |
Hans-Peter Diettrich Guest
|
Posted: Mon Jan 16, 2006 6:39 pm Post subject: Re: Just sharing a coding trick |
|
|
Jamie schrieb:
| Quote: | here is a trick i use to prevent re'entry of the code block.
procedure Tform1.ScrollBar1.Change(Sender:TObject);
Const Busy :Boolean = false; // make sure write able consts are on..
Begin
If Busy then exit else busy := true;
// do your code...
Busy := false;
End;
|
I don't like such code for two reasons:
Writeable constants are bad, this option allows to overwrite other
constant values as well. A state variable is okay, of course, and it
should be visible within all related procedures. Sometimes a lock
counter is ueseful, as used with BeginUpdate and EndUpdate.
| Quote: | If Busy then exit else busy := true;
If the following code is interrupted, perhaps due to an exception, Busy |
will stay true forever. A variable with a wider visibility can be reset
by other code, in contrast to a local constant.
DoDi
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Tue Jan 17, 2006 9:05 am Post subject: Re: Just sharing a coding trick |
|
|
"Hans-Peter Diettrich" <DrDiettrich (AT) nowhere (DOT) nix> wrote
| Quote: | Jamie schrieb:
here is a trick i use to prevent re'entry of the code block.
procedure Tform1.ScrollBar1.Change(Sender:TObject);
Const Busy :Boolean = false; // make sure write able consts are on..
Begin
If Busy then exit else busy := true;
// do your code...
Busy := false;
End;
I don't like such code for two reasons:
Writeable constants are bad, ...
|
Fully agreed. "Const" is for *constant*.
| Quote: | If Busy then exit else busy := true;
If the following code is interrupted, perhaps due to an exception,
Busy will stay true forever. A variable with a wider visibility can
be reset by other code, in contrast to a local constant.
|
True, and there should have been a try-finally in the procedure.
I wouldn't have used Exit for this, either.
But this is a local lock and you don't really want it visible outside
the procedure; you just want it done properly inside it. And you can't.
Writeable constants are too high a price to pay; I'd use a global
variable. But it is a choice between two evils.
Groetjes,
Maarten Wiltink
|
|
| Back to top |
|
 |
Paul Dunn Guest
|
Posted: Tue Jan 17, 2006 9:44 pm Post subject: Re: Just sharing a coding trick |
|
|
Tom de Neef wrote:
| Quote: | It's a bit like enabling and disabling application interrupts, as in:
procedure TMainForm.AppMessage(var Msg: TMsg; var Handled: Boolean);
// trap all user actions and dump them except an ESC key press
begin
if (Msg.wParam<>27) AND hintWindow.IsHintMsg(Msg)
then Handled := True;
end;
|
But wouldn't that still interrupt the flow of your application (assuming
that your calculation allows application.processmessages() during it's work)
when a message arrives?
I'm a tad confused about that - surely the best way to prevent a
time-critical calculation from being interrupted is to ignore all windows
messages until it's done? Use an API function to get the status of the ESC
key periodically instead?
D.
|
|
| Back to top |
|
 |
Tom de Neef Guest
|
Posted: Tue Jan 17, 2006 10:55 pm Post subject: Re: Just sharing a coding trick |
|
|
"Paul Dunn" <paul.dunn4 (AT) ntlworld (DOT) com> schreef in bericht
news:Uydzf.302$N66.154 (AT) newsfe2-win (DOT) ntli.net...
| Quote: | Tom de Neef wrote:
It's a bit like enabling and disabling application interrupts, as in:
procedure TMainForm.AppMessage(var Msg: TMsg; var Handled: Boolean);
// trap all user actions and dump them except an ESC key press
begin
if (Msg.wParam<>27) AND hintWindow.IsHintMsg(Msg)
then Handled := True;
end;
But wouldn't that still interrupt the flow of your application (assuming
that your calculation allows application.processmessages() during it's
work) when a message arrives?
I'm a tad confused about that - surely the best way to prevent a
time-critical calculation from being interrupted is to ignore all windows
messages until it's done? Use an API function to get the status of the ESC
key periodically instead?
D.
|
When it is time-critical, yes.
But there are also cases where you just don't want the user to change
controls during a calculation, but still want the application to change the
controls (eg update a progress bar). And you want to keep open the Esc
route, in case the calculation time runs out of hand.
Tom
|
|
| Back to top |
|
 |
|