EDIT:
Im having to edit some old Pascal Script in Fast Reports and are having a tough time returning only distinct records. Any help would be appreciated.
So Basically, I am calling a stored proc to return data in a table. In one of these columns there sometimes are a 1 letter code (A,B,C) that defines which rtf file to go fetch for the report.
Currently It does go fetch all of the rtf files for the respective codes, but sometimes the code is repeated (A,A) and in that case I need it to return only the DISTINCT rtf files. So If A was pulled allready, Dont Pull again and carry on looking for other codes in that Column
My Code:
NOTE: In Memo93 I am just inserting (table."Class")
procedure GroupHeader17OnBeforePrint(Sender: TfrxComponent );
var
CarGroup: String ;
CarGroupLoop: String;
begin
CarGroup := Memo93.Value;
if not(CarGroup = CarGroupLoop) then
begin
try
GroupHeader17.Visible := TRUE;
rich23.richedit.lines.LoadFromFile('example\'+ trim(Get('#QteLanguage'))+ '\'+ trim(Get('#QteLanguage'))+'_Group_'+ trim(<Table."class">)+ '.rtf');
except
GroupHeader17.Visible := FALSE;
end;
end
else
begin
GroupHeader17.Visible := False;
end;
CarGroupLoop := <table."Class">;
end;
This Seem to have done the job and only bring back distinct rtf files.
procedure GroupHeader17OnBeforePrint(Sender: TfrxComponent );
var
CarGroup: String ;
CarGroupLoop: String;
begin
CarGroup := Memo93.Value;
CarGroupLoop := <table."Class">;
if not(CarGroup = CarGroupLoop) then
begin
try
GroupHeader17.Visible := TRUE;
rich23.richedit.lines.LoadFromFile('example\'+ trim(Get('#QteLanguage'))+ '\'+ trim(Get('#QteLanguage'))+'_Group_'+ trim( <table."Class">)+ '.rtf');
except
GroupHeader17.Visible := FALSE;
end;
end
else
begin
GroupHeader17.Visible := False;
end;
end;
Try to use CarGroup := <table."Class">; and move CarGroupLoop := <table."Class">; at the end of GroupHeader17OnBeforePrint event
procedure GroupHeader17OnBeforePrint(Sender: TfrxComponent );
var
CarGroup: String ;
CarGroupLoop: String;
begin
CarGroup := <table."Class">;
if not(CarGroup = CarGroupLoop) then
begin
try
GroupHeader17.Visible := TRUE;
rich23.richedit.lines.LoadFromFile('example\'+ trim(Get('#QteLanguage'))+ '\'+ trim(Get('#QteLanguage'))+'_Group_'+ trim(<Table."class">)+ '.rtf');
except
GroupHeader17.Visible := FALSE;
end;
end
else
begin
GroupHeader17.Visible := False;
end;
CarGroupLoop := <table."Class">;
end;
Try
var CarGroup: String; //global variable
procedure GroupHeader17OnBeforePrint(Sender: TfrxComponent);
var CarGroupLoop: String ;
begin
CarGroupLoop := trim(<rpt_1_RHINO_Costing_Curr_Totals."SupplierClassCodeTransport">);
if not(CarGroup = CarGroupLoop) then
begin
try
//rich23.richedit.lines.LoadFromFile('D:\Data\Shares\GlobeTrackNew\QuoteInfo\Suppliers\Bidvest\'+ trim(Get('#QteLanguage'))+ '\'+ trim(Get('#QteLanguage'))+'_Group_'+ trim(<rpt_1_RHINO_Costing_Curr_Totals."SupplierClassCodeTransport">)+ '.rtf');
GroupHeader17.Visible := TRUE;
//rich23.richedit.lines.LoadFromFile('D:\Data\Shares\GlobeTrackNew\QuoteInfo\Suppliers\Bidvest\GER\GER_Group_I.rtf');
except
GroupHeader17.Visible := FALSE;
end;
end
else
GroupHeader17.Visible := FALSE;
CarGroup := trim(<rpt_1_RHINO_Costing_Curr_Totals."SupplierClassCodeTransport">);
end;
procedure SubCarHireOnBeforePrint(Sender: TfrxComponent);
begin
CarGroup := '';
end;
Related
I wrote this redundant code consisting of 30 lines:
if Button = TMouseButton.mbLeft then
begin
if pnlEndColor.ShowCaption then
begin
pnlStartColor.ShowCaption := False;
pnlEndColor.ShowCaption := False;
pnlStartColor.Color := ThisColor;
pnlEndColor.Color := ThisColor;
end
else
begin
pnlStartColor.ShowCaption := False;
pnlStartColor.Color := ThisColor;
end;
end
else if Button = TMouseButton.mbRight then
begin
if pnlStartColor.ShowCaption then
begin
pnlStartColor.ShowCaption := False;
pnlEndColor.ShowCaption := False;
pnlStartColor.Color := ThisColor;
pnlEndColor.Color := ThisColor;
end
else
begin
pnlEndColor.ShowCaption := False;
pnlEndColor.Color := ThisColor;
end;
end;
I manually refactored the code by extracting it to a small method by applying just logic:
procedure TForm1.SetPanelColors(Panel1, Panel2: TPanel; const aColor: TColor);
begin
if Panel2.ShowCaption then
begin
Panel1.ShowCaption := False;
Panel2.ShowCaption := False;
Panel1.Color := aColor;
Panel2.Color := aColor;
end
else
begin
Panel1.ShowCaption := False;
Panel1.Color := aColor;
end;
end;
Then I used the method by these 4 lines of code (Savings of 26 lines compared to the previous redundant code):
if Button = TMouseButton.mbLeft then
SetPanelColors(pnlStartColor, pnlEndColor, ThisColor)
else
SetPanelColors(pnlEndColor, pnlStartColor, ThisColor);
How could such a refactoring of redundant code be automated? Are there any libraries or general resources for such a purpose?
I have such a code
Label1.Caption := '';
Label2.Caption := '';
Label3.Caption := '';
Label4.Caption := '';
Label5.Caption := '';
Label6.Caption := '';
How can I make a loop or sth to make it shorter?
Make simple procedure to work on array of labels:
procedure ClearLabels(LabelsArr: array of TLabel);
var
i: Integer;
begin
for i := Low(LabelsArr) to High(LabelsArr) do
LabelsArr[i].Caption := '';
end;
and call it like that:
ClearLabels([Label1, Label2, Label3]);
I am having trouble getting the serial port data from an equipment.
Below is the image of the expected result:
Desire result:
Unwanted result:
I use Ttimer so I can automatically get the data and put it to the Memo.
I need the data to be placed line by line in the memo.
This is the source code:
procedure TForm3.Timer1Timer(Sender: TObject);
var
k: Integer;
InBuffer: array[1..500] of char;
begin
for k:=1 to 500 do
InBuffer[k]:=' ';
Trim(InBuffer);
if cport.Connected = true then
begin
ComLed1.Kind := lkGreenLight;
cport.ReadStr(str,k);
Trim(str);
S:=str;
if str = '' then
begin
end
else
begin
memo1.lines.Add(str);
end;
end
else
begin
ComLed1.Kind := lkredLight;
txt_com_status1.Caption := 'Connected';
end;
end;
My question is what is the problem? And what is the solution for this.
TMemo.Lines.Add() adds a line. The text you add will have a line break inserted at the end of it. It is clear that you are receiving the hardware data in pieces, and you are adding each piece separately as its own line in the Memo.
To do what you are attempting, you need to either:
Read the pieces from the hardware and cache them until you detect the end of a complete message, and then Add() only complete messages to the Memo. How you do this depends on the particular protocol the hardware is using to send data to you. Does it wrap the data in STX/ETX markers? Does it delimit messages? We don't know, you have not provided any information about that. And your code is trying (unsuccessfully) to trim a lot of data away that it probably shouldn't be throwing away at all.
Don't use Add() at all. You can use the SelText property instead to avoid inserting any line breaks you don't want.
memo1.SelStart := memo1.GetTextLen;
memo1.SelLength := 0;
memo1.SelText := str;
That being said, your timer code is doing some odd things. InBuffer is filled with spaces, then (unsuccessfully) trimmed, and then completely ignored. You are passing an uninitialized k value to ReadStr(). The str value you do read is unsuccessfully trimmed before added to the Memo. You are assigning str to S and then ignoring S.
Try this instead:
procedure TForm3.Timer1Timer(Sender: TObject);
var
str: AnsiString;
begin
if cport.Connected then
begin
ComLed1.Kind := lkGreenLight;
txt_com_status1.Caption := 'Connected';
cport.ReadStr(str, 256);
str := Trim(str);
if str <> '' then
begin
memo1.SelStart := memo1.GetTextLen;
memo1.SelLength := 0;
memo1.SelText := str;
end;
end
else
begin
ComLed1.Kind := lkredLight;
txt_com_status1.Caption := 'Disconnected';
end;
end;
Alternatively (assuming you are using TComPort that has an OnRxChar event):
procedure TForm3.Timer1Timer(Sender: TObject);
begin
if cport.Connected then
begin
ComLed1.Kind := lkGreenLight;
txt_com_status1.Caption := 'Connected';
end
else
begin
ComLed1.Kind := lkredLight;
txt_com_status1.Caption := 'Disconnected';
end;
end;
procedure TForm3.cportRxChar(Sender: TObject; Count: Integer);
var
str: AnsiString;
begin
cport.ReadStr(str, Count);
str := Trim(str);
if str <> '' then
begin
memo1.SelStart := memo1.GetTextLen;
memo1.SelLength := 0;
memo1.SelText := str;
end;
end;
Edit based on new information provided in comments, try something like this:
private
buffer: AnsiString;
portConnected: boolean;
procedure TForm3.Timer1Timer(Sender: TObject);
begin
if cport.Connected then
begin
if not portConnected then
begin
portConnected := true;
buffer := '';
ComLed1.Kind := lkGreenLight;
txt_com_status1.Caption := 'Connected';
end;
end
else
begin
if portConnected then
begin
portConnected := false;
ComLed1.Kind := lkredLight;
txt_com_status1.Caption := 'Disconnected';
end;
end;
end;
procedure TForm3.cportRxChar(Sender: TObject; Count: Integer);
var
str: AnsiString;
i: integer;
begin
cport.ReadStr(str, Count);
buffer := buffer + str;
repeat
i := Pos(#10, buffer);
if i = 0 then Exit;
str := Copy(buffer, 1, i-1);
Delete(buffer, 1, i);
memo1.Lines.Add(str);
until buffer = '';
end;
I have code that creates summary footers at runtime for numeric columns, but I can't get the group summary results to show. I've looked at How to create group summaries at runtime and How to set group summary values and How can create summary footer on runtime? but I'm hitting runtime error:
EcxInvalidDataControllerOperation with message 'RecordIndex out of range'
when the grid is rendering.
This code accepts any TcxGridDBTableView so it would be very easy to put into an existing Delphi form.
procedure SummaryGroup(ASummary: TcxDataSummary; AColumn: TcxGridDBColumn;
AKind: TcxSummaryKind; AFormat: string);
var
sumGroup: TcxDataSummaryGroup;
link: TcxGridTableSummaryGroupItemLink; //TcxDataSummaryGroupItemLink;
item: TcxGridDBTableSummaryItem;
begin
AColumn.Summary.FooterKind := AKind;
AColumn.Summary.FooterFormat := AFormat;
sumGroup := ASummary.SummaryGroups.Add;
link := sumGroup.Links.Add as TcxGridTableSummaryGroupItemLink;
link.Column := AColumn;
item := sumGroup.SummaryItems.Add as TcxGridDBTableSummaryItem;
item.Column := AColumn;
item.Kind := AKind;
item.Position := spGroup;
item.Format := AColumn.Summary.FooterFormat;
end;
procedure AutoAwesum(AView: TcxGridDBTableView);
var
summary: TcxDataSummary;
summing: Boolean;
i: Integer;
dc: TcxGridDBDataController;
col: TcxGridDBColumn;
begin
dc := AView.DataController;
summing := False;
summary := dc.Summary;
summary.BeginUpdate;
try
summary.SummaryGroups.Clear;
dc.BeginFullUpdate;
try
dc.GridView.ClearItems;
dc.CreateAllItems;
for i := 1 to AView.ColumnCount - 1 do
begin
col := AView.Columns[i];
case col.DataBinding.Field.DataType of
ftSmallint, ftInteger, ftWord, ftLargeint, ftAutoInc,
ftLongWord, ftShortint:
begin
summing := true;
SummaryGroup(summary, col, skSum, '#');
end;
ftFloat, ftBCD, ftFMTBcd, ftExtended, ftSingle:
begin
summing := true;
SummaryGroup(summary, col, skSum, '#.##');
end;
ftCurrency:
begin
summing := true;
SummaryGroup(summary, col, skSum, '$#.##');
end;
end;
end;
dc.DataModeController.GridMode := not summing;
AView.OptionsView.Footer := summing;
AView.OptionsView.GroupFooterMultiSummaries := summing;
AView.OptionsView.GroupFooters := gfVisibleWhenExpanded;
finally
dc.EndFullUpdate;
end;
finally
summary.EndUpdate;
end;
end;
What am I missing? Thanks.
Finally had a chance to get back to this. As expected, the changes were few and simple. Here's the code that generically creates group summary headers for each numeric column in a grid. I've left some options commented out in the code that you may want to use.
uses
cxGridDBDataDefinitions;
procedure Summarize(ASummary: TcxDataSummary; AColumn: TcxGridDBColumn;
AKind: TcxSummaryKind; AFormat: string);
var
sumGroup: TcxDataSummaryGroup;
link: TcxGridTableSummaryGroupItemLink;
item: TcxGridDBTableSummaryItem;
begin
AColumn.Summary.FooterKind := AKind;
AColumn.Summary.FooterFormat := AFormat;
AColumn.Summary.GroupKind := AKind;
AColumn.Summary.GroupFormat := AFormat;
AColumn.GroupIndex := -1;
sumGroup := ASummary.SummaryGroups.Add;
link := sumGroup.Links.Add as TcxGridTableSummaryGroupItemLink;
link.Column := AColumn;
item := sumGroup.SummaryItems.Add as TcxGridDBTableSummaryItem;
item.Column := AColumn;
item.Kind := skSum;
item.Position := spGroup;
item.Format := AColumn.Summary.FooterFormat;
end;
procedure AutoAwesum(AView: TcxGridDBTableView);
var
summary: TcxDataSummary;
summing: Boolean;
i: Integer;
dc: TcxGridDBDataController;
col: TcxGridDBColumn;
begin
dc := AView.DataController;
summing := False;
summary := dc.Summary;
summary.BeginUpdate;
try
summary.SummaryGroups.Clear;
dc.BeginFullUpdate;
try
dc.GridView.ClearItems;
dc.CreateAllItems;
for i := 1 to AView.ColumnCount - 1 do
begin
col := AView.Columns[i];
case col.DataBinding.Field.DataType of
ftSmallint, ftInteger, ftWord, ftLargeint, ftAutoInc,
ftLongWord, ftShortint:
begin
summing := true;
Summarize(summary, col, skSum, ',0');
end;
ftFloat, ftBCD, ftFMTBcd, ftExtended, ftSingle:
begin
summing := true;
Summarize(summary, col, skSum, ',.00');
end;
ftCurrency:
begin
summing := true;
Summarize(summary, col, skSum, '$,0.00');
end;
end;
end;
// dc.DataModeController.GridMode := not summing;
// AView.OptionsView.Header := summing;
AView.OptionsView.Footer := summing;
// AView.OptionsView.GroupFooterMultiSummaries := summing;
// AView.OptionsView.GroupFooters := gfVisibleWhenExpanded;
finally
dc.EndFullUpdate;
end;
finally
summary.EndUpdate;
end;
end;
I want to check and see if the TEdit.text is in this format 123/45/678 When text was entered
Thus ###/##/###
any simple way to do this?
thanks
Function CheckStringWithMask(const Str,Mask:String):Boolean;
var
i:Integer;
begin
Result := true;
if length(str)=length(Mask) then
begin
i := 0;
While Result and (I < Length(Str)) do
begin
inc(i);
Result := Result and (Str[i] <> '#')
and ((Mask[i] ='#') and (CharInSet(Str[i],['0'..'9']))
or (Str[i]=Mask[i]));
end;
end
else Result := false;
end;
Assuming your mask is so simple that it only has # and / it's easy to write a test function:
function MatchesMask(const Text, Mask: string): Boolean;
var
i: Integer;
begin
Result := False;
if Length(Text)<>Length(Mask) then
exit;
for i := 1 to Length(Text) do
case Mask[i] of
'#':
if (Text[i]<'0') or (Text[i]>'9') then
exit;
else
if Text[i]<>Mask[i] then
exit;
end;
Result := True;
end;
A variation on #David Heffernan's suggestion:
function MatchesMask(const Text, Mask: string): Boolean;
var
i: Integer;
begin
Result := (Length(Text) = Length(Mask));
if not Result then Exit;
i := 0;
while Result and (i < Length(Text)) do begin
Inc(i);
case Mask[i] of
'#':
Result := (Text[i] >= '0') and (Text[i] <= '9');
else
Result := (Text[i] = Mask[i]);
end;
end;
end;