It also does not seem to work. If I do not set the ItemIndex, everything
seems to work, but the RadioGroup is incorrect.
Well, I got something that seemed to work, but it seems very wrong, and I'm
too tired to think clearly. Here's what I came up with:
//****************************** SetTestType
*********************************
procedure TfmOrt.SetTestType(Mode: TTestMode);
begin
if Mode = NORMAL then
TestTypeSel.ItemIndex := 0
else
TestTypeSel.ItemIndex := 1;
// TestTypeSelClick(self);
// This was superfluous, as cahnging the ItemIndex fired the event
end;
//**************************** TestTypeSelClick
*****************************
procedure TfmOrt.TestTypeSelClick(Sender: TObject);
begin
fmDebug.AddDebugItem( 'SetTestType ' );
if fmSetup.TestMode = NORMAL then begin
fmWaveform.WaveFileName := NORMWAVEFILE;
MaxOps := NormalMaxOps;
OrtUpdate1.OnThreshold := fmCal.Cal.OnThresh;
cbMaxOps.Enabled := True;
MinPickUpCurrent.Visible := FALSE;
MinPUlabel.Visible := FALSE;
fmOrtMeter.Visible := False;
end
else begin
fmWaveform.WaveFileName := MPUWAVEFILE;
MaxOps := 1;
OrtUpdate1.OnThreshold := fmCal.Cal.MPUThresh;
cbMaxOps.Enabled := False;
MinPickUpCurrent.Text := '';
MinPickUpCurrent.Visible := TRUE;
MinPUlabel.Visible := TRUE;
fmOrtMeter.Visible := True;
if fmSetup.MaxOnTime < 10 then
fmSetup.MaxOnTime := 10;
end;
SetMaxOps( MaxOps );
ResetData;
fmDebug.AddDebugItem( 'End SetTestType ' );
end;
Originally I had the SetTestType procedure do what is now done in the
TestTypeSelClick handler, as well as setting the ItemIndex. Now it only sets
the ItemIndex. But it still doesn't work right. I've tried all sorts of
things to no avail. This seems like a simple thing. I'll try a little more,
but I'm about beat. I'll follow up if I find a solution. Any ideas?
Thanks,
Paul
You are taking the radiogroup, which is sort of a visual property, and
wrapping it in a property of your own (TestType, of type TTestMode).
Excellent. Nothing wrong with it.
You get notified whenever the radiogroup's itemindex changes. Just what
you want. Don't fight it.
The TestType property is changed, and Magic Happens. No trying to
change the TestType back, or anything to the radiogroup itself, just
nice outward-pointing resynchronisation of related elements. All nice
and clean.
> Originally I had the SetTestType procedure do what is now done in the
> TestTypeSelClick handler, as well as setting the ItemIndex. Now it only
> sets the ItemIndex. But it still doesn't work right.
How? There is nothing wrong with the above code.
If you want to delay the reaction to the change in TestType's value, you
could add a flag for that, in a standard Begin/EndUpdate pattern. But I
really see no reason why the above 'doesn't work'.
Groetjes,
Maarten Wiltink
I just discovered that there is a problem when I show another form which
contains an "Analog Meter", which is a freeware component I got from a
download. Here is the header info:
Written 1999 by Hannes Breuer (hannes...@talknet.de)
inspired by Andrey Abakumov (a...@novoch.ru)
For some reason, as soon as I set focus back to my main form, the OnClick
event fires.
The component is available here: http://www.thebreuery.com/delphi.html
That is a newer version with extensive updates, but using it didn't change
the problem. I don't have it installed as a component. I just included the
source in my project and I used the Create and Destroy methods:
public
AnalogMeter1: TAnalogMeter;
procedure TfmOrtMeter.FormCreate(Sender: TObject);
begin
AnalogMeter1 := TAnalogMeter.Create(Self);
with AnalogMeter1 do begin
Parent := fmOrtMeter;
Align := alClient; //allows testing resize
Left := 8;
Top := 5;
Width := 262;
Height := 234;
Anchors := [akLeft, akTop, akRight, akBottom];
Color := clBtnFace;
Font.Charset := DEFAULT_CHARSET;
Font.Color := clWindowText;
Font.Height := -11;
Font.Name := 'MS Sans Serif';
Font.Style := [];
ParentColor := False;
Caption := '% FS x 10';
Max := 1000;
TickCount := 21;
LowZone := 20;
HighZone := 80;
OKZoneColor := clLime;
end;
end;
procedure TfmOrtMeter.FormDestroy(Sender: TObject);
begin
AnalogMeter1.Destroy;
end;
procedure TfmOrtMeter.FormShow(Sender: TObject);
begin
if Button1.Caption = 'Large' then
fmOrtMeter.Left := (Screen.Width div 2) + (fmOrt.Width div 2) + 2
else
fmOrtMeter.Left := Screen.Width - fmOrtMeter.Width - 1;
end;
I have a linear bargraph built into my main form, but I wanted a circular
meter for easier readability. This problem may have existed for quite a
while, as I had not tested this particular sequence of events. The problem
occurs only when the application starts in the MinPU mode, which is
ItemIndex[1]. When the event fires, it always reads the ItemIndex as zero,
so it gets set back to NORMAL mode. After that, I can switch back and forth
by selecting either radio button and all is OK, until the next time the
application starts.
Thanks for looking at this.
Paul
This *should* not be a problem, but getting it right can be more than
a little tricky.
Naively, the event fires when the property's value has changed. It is
tempting to make the assumption that this means that you went from one
valid value to another valid value. But that assumption is a little too
strong.
What the event really is, is a notification that you need to update
your related elements - without assuming that they are in 'the other'
state (your property is essentially Boolean). If for example you have
a panel whose visibility should follow the property, don't blindly
toggle its visibility in the event handler. _Set_ its visibility to
the appropriate value instead. Do not make assumptions about the
previous value. Write stateless code. I hope that makes it clear enough
for you.
> [...] The problem occurs only when the application starts in the MinPU
> mode, which is ItemIndex[1]. When the event fires, it always reads the
> ItemIndex as zero, so it gets set back to NORMAL mode. After that, I
> can switch back and forth by selecting either radio button and all is
> OK, until the next time the application starts.
How does the application start in MinPU mode, if not by setting the
itemindex to 1? You have shown the property setter; it forwards the
value to the radiogroup, which handles the necessary updates. The getter
should read the value from the radiogroup. There is (well, there should
be) no way for TestType to be MinPU except by the radiogroup having its
second button checked.
If you are 'entering MinPU mode' by carefully duplicating all the
_effects_ of switching to it, please throw away that code NOW. Your
radiogroup comes up in a certain state; call that event handler once
at startup and your application should end up correctly initialised.
(Ideally, the radiogroup should be loaded with itemindex=(-1), it should
be cheap to configure application state for that, and restoring settings
would result in updating related elements exactly once.)
Groetjes,
Maarten Wiltink
Whyever not <g>
TestTypeSel.OnClick := nil;
TestTypeSel.ItemIndex := 1;
TestTypeSel.OnClick ;= TestTypeSelClick;
Works for me.
If you need to do other things, like calling the OnClick once then
var
SavedOnClick : TNotifyEvent;
TestTypeSel.OnClick := SavedOnClick;
TestTypeSel.ItemIndex := 1;
SavedOnClick(nil); // or SavedOnClick(TestTypeSel); if you use Sender
TestTypeSel.OnClick ;= TestTypeSelClick;
Alan Lloyd
I simply preset the ItemIndex=(-1), and now it works OK. Originally, I had
the SetTestType(Mode) procedure take care of all the changes needed, and the
TestTypeSelClick handler simply read the ItemIndex and called
SetTestType(Mode) with the corresponding parameter. But the SetTestType
procedure also set the ItemIndex, which may have caused a recursive state.
In my old code, I had essentially duplicated the SetTestType procedure in
the event handler, so it did not cause the second firing of the event. My
efforts to streamline my code wound up shooting me in the foot. No good deed
goes unpunished, and the smallest bugs seem to be the toughest to squash.
It seems a bit clearer now. Amazing what can be accomplished after a few
hours sleep, a couple cups of coffee, and help from you guys!
Thanks!
Paul
I found a couple more things that I needed to do before this worked
properly. My call to the SetTestType routine would not fire the OnClick
event if there was no change in the ItemIndex. So I did this:
procedure TfmOrt.SetTestType(Mode: TTestMode);
begin
TestTypeSel.ItemIndex := -1; //This does not trigger an OnClick event
if Mode = NORMAL then
TestTypeSel.ItemIndex := 0 //But now these are guaranteed to
else
TestTypeSel.ItemIndex := 1;
end;
There is still a lot of ugly code in this project, and I am loathe to clean
it up if it works. It seems like a nightmare whack-a-mole game, where you
squash one bug and two more appear elsewhere. Some of this code dates back
to around 2003, when I was just learning Delphi and OOP. I have a better
grasp now, but still there are concepts I may not fully understand.
This has been a difficult, but highly interesting project. Now it includes
aspects of threads, real-time programming (communications handling) using
either a virtual COM port or direct USB meessaging, as well as database and
report generation. Quite a learning experience. I appreciate all the help
given on this forum. And I can't contribute much more than additional
problems for you guys, but I hope you enjoy the challenges to some extent.
Thanks!
Paul
I still get the feeling that you are fighting Delphi, and doing things
that Delphi does well (& ought to be doing) if you just give it a bit
of direction.
If I was were you are I'd start with a blank sheet of paper & jot some
new ideas of the overall system & code design. Keep a copy of the old
code so you can revert to it if cleaner, clearer, better code does not
ensue.
A sneaking feeling that one's code is overly complex & not elegant, is
usually correct for anyone who has done some serious coding.
Alan Lloyd
I am honest enough to give the few hours of sleep most of the credit
there.
> I found a couple more things that I needed to do before this worked
> properly. My call to the SetTestType routine would not fire the
> OnClick event if there was no change in the ItemIndex. So I did this:
>
> procedure TfmOrt.SetTestType(Mode: TTestMode);
> begin
> TestTypeSel.ItemIndex := -1; //This does not trigger an OnClick event
> if Mode = NORMAL then
> TestTypeSel.ItemIndex := 0 //But now these are guaranteed to
> else
> TestTypeSel.ItemIndex := 1;
> end;
>
> There is still a lot of ugly code in this project, ...
And there is more now. What you do above is not just ugly, it's wrong.
A property should not fire OnChange if its hasn't changed. Conversely,
manually firing OnChange should be harmless and bring everything in
line with the current property value again. As I said, manually firing
the event at program startup may be required to guarantee a valid
initial state.
Groetjes,
Maarten Wiltink