From 72986ddc75cd0356906a8a95965ee8b96b75015a Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Fri, 26 Jun 2020 16:36:32 +0000 Subject: [PATCH] fpspreadsheet: top10 in conditional formatting. More advanced demo. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7501 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../demo_conditional_formatting.pas | 258 +++++++++++++++++- .../source/common/fpsconditionalformat.pas | 7 +- .../fpspreadsheet/source/common/fpstypes.pas | 17 +- .../fpspreadsheet/source/common/xlsxooxml.pas | 108 +++++++- 4 files changed, 374 insertions(+), 16 deletions(-) diff --git a/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas b/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas index 8fd4d1f61..ef8a35a7f 100644 --- a/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas +++ b/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas @@ -10,11 +10,264 @@ var fmt: TsCellFormat; fmtIdx: Integer; font: TsFont; - + row: Integer; + i: Integer; + lastCol: Integer; begin wb := TsWorkbook.Create; try sh := wb.AddWorksheet('test'); + sh.WriteDefaultColWidth(20, suMillimeters); + + sh.WriteText(0, 0, 'Condition'); + sh.WriteColWidth(0, 50, suMillimeters); + sh.WriteText(0, 1, 'Format'); + sh.WriteColWidth(1, 70, suMillimeters); + sh.WriteText(0, 2, 'Test values'); + + row := 2; + for i := row to row+30 do + begin + sh.WriteNumber(i, 2, 1.0); + sh.WriteNumber(i, 3, 2.0); + sh.WriteNumber(i, 4, 3.0); + sh.WriteNumber(i, 5, 4.0); + sh.WriteNumber(i, 6, 5.0); + sh.WriteNumber(i, 7, 6.0); + sh.WriteNumber(i, 8, 7.0); + sh.WriteNumber(i, 9, 8.0); + sh.WriteNumber(i, 10, 9.0); + sh.WriteNumber(i, 11, 10.0); + sh.WriteText(i, 12, 'abc'); + sh.WriteText(i, 13, 'abc'); + sh.WriteBlank(i, 14); +// sh.WriteText(i, 14, ''); + sh.WriteText(i, 15, 'def'); + sh.WriteText(i, 16, 'defg'); + sh.WriteFormula(i, 17, '=1.0/0.0'); + sh.WriteFormula(i, 18, '=1.0/1.0'); + end; + lastCol := 18; + + // conditional format #1: equal to number constant + sh.WriteText(row, 0, 'equal to constant 5'); + sh.WriteText(row, 1, 'background yellow'); + + // prepare cell format tempate + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scYellow); + fmtIdx := wb.AddCellFormat(fmt); + // Write conditional format + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcEqual, 5, fmtIdx); + + // conditional format #2: equal to text constant + inc(row); + sh.WriteText(row, 0, 'equal to text "abc"'); + sh.WriteText(row, 1, 'background green'); + fmt.SetBackgroundColor(scGreen); + fmtIdx := wb.AddCellFormat(fmt); + // Write conditional format + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcEqual, 'abc', fmtIdx); + + // conditional format #3: greater than cell reference + inc(row); + sh.WriteText(row, 0, 'greater than cell C3'); + sh.WriteText(row, 1, 'all borders, red, thick line'); + InitFormatRecord(fmt); + fmt.SetBorders([cbEast, cbWest, cbNorth, cbSouth], scRed, lsThick); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcGreaterThan, 'C3', fmtIdx); + + // conditional format #4: less than formula + inc(row); + sh.WriteText(row, 0, 'less than formula "=1+3"'); + sh.WriteText(row, 1, 'background red'); + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scRed); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcLessThan, '=1+3', fmtIdx); + + // conditional format #5: greater equal constant + inc(row); + sh.WriteText(row, 0, 'greater equal constant 5'); + sh.WriteText(row, 1, 'background gray'); + fmt.SetBackgroundColor(scGray); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcGreaterEqual, 5, fmtIdx); + + // conditional format #6: less equal constant + inc(row); + sh.WriteText(row, 0, 'less equal constant 5'); + sh.WriteText(row, 1, 'background gray'); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcLessEqual, 5, fmtIdx); + + // conditional format #6: between + inc(row); + sh.WriteText(row, 0, 'between 3 and 7'); + sh.WriteText(row, 1, 'background light gray'); + fmt.SetBackgroundColor($EEEEEE); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcBetween, 2, 7, fmtIdx); + + // conditional format #6: not between + inc(row); + sh.WriteText(row, 0, 'not between 3 and 7'); + sh.WriteText(row, 1, 'background light gray'); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcNotBetween, 2, 7, fmtIdx); + + // conditional format #6: above average + inc(row); + sh.WriteText(row, 0, '> average'); + sh.WriteText(row, 1, 'hatched background yellow on red'); + InitFormatRecord(fmt); + fmt.SetBackground(fsThinStripeDiagUp, scRed, scYellow); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcAboveAverage, fmtIdx); + + // conditional format #6: below average + inc(row); + sh.WriteText(row, 0, '< average'); + sh.WriteText(row, 1, 'dotted background yellow on red'); + InitFormatRecord(fmt); + fmt.SetBackground(fsGray25, scRed, scYellow); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcBelowAverage, fmtIdx); + + // conditional format #6: above or equal to average + inc(row); + sh.WriteText(row, 0, '>= average'); + sh.WriteText(row, 1, 'hor striped background yellow on red'); + InitFormatRecord(fmt); + fmt.SetBackground(fsThinStripeHor, scRed, scYellow); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcAboveEqualAverage, fmtIdx); + + // conditional format #6: below or equal to average + inc(row); + sh.WriteText(row, 0, '<= average'); + sh.WriteText(row, 1, 'vert striped background yellow on red'); + InitFormatRecord(fmt); + fmt.SetBackground(fsThinStripeVert, scRed, scYellow); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcBelowEqualAverage, fmtIdx); + + // conditional format #6: top 3 values + inc(row); + sh.WriteText(row, 0, 'top 3 values'); + sh.WriteText(row, 1, 'background green'); + fmt.SetBackgroundColor(scGreen); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcTop, 3, fmtIdx); + + // conditional format #6: smallest 3 values + inc(row); + sh.WriteText(row, 0, 'smallest 3 values'); + sh.WriteText(row, 1, 'background bright blue'); + fmt.SetBackgroundColor($FFC0C0); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcBottom, 3, fmtIdx); + + // conditional format #6: top 30 percent + inc(row); + sh.WriteText(row, 0, 'top 10 percent'); + sh.WriteText(row, 1, 'background green'); + fmt.SetBackgroundColor(scGreen); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcTopPercent, 10, fmtIdx); + + // conditional format #6: smallest 3 values + inc(row); + sh.WriteText(row, 0, 'smallest 10 percent'); + sh.WriteText(row, 1, 'background bright blue'); + fmt.SetBackgroundColor($FFC0C0); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcBottomPercent, 10, fmtIdx); + + // conditional format #6: duplicates + inc(row); + sh.WriteText(row, 0, 'duplicate values'); + sh.WriteText(row, 1, 'background bright red'); + fmt.SetBackgroundColor($D0D0FF); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcDuplicate, fmtIdx); + + // conditional format #6: unique + inc(row); + sh.WriteText(row, 0, 'unique values'); + sh.WriteText(row, 1, 'background bright red'); + fmt.SetBackgroundColor($D0D0FF); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcUnique, fmtIdx); + + // conditional format #6: contains any text + inc(row); + sh.WriteText(row, 0, 'contains any text'); + sh.WriteText(row, 1, 'background red'); + fmt.SetBackgroundColor(scRed); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcContainsText, '', fmtIdx); + + // conditional format #6: empty + inc(row); + sh.WriteText(row, 0, 'empty'); + sh.WriteText(row, 1, 'background red'); + fmt.SetBackgroundColor(scRed); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcNotContainsText, '', fmtIdx); + + // conditional format #6: text begins with 'ab' + inc(row); + sh.WriteText(row, 0, 'text begins with "ab"'); + sh.WriteText(row, 1, 'background red'); + fmt.SetBackgroundColor(scRed); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcBeginsWith, 'ab', fmtIdx); + + // conditional format #6: text ends with 'g' + inc(row); + sh.WriteText(row, 0, 'text ends with "g"'); + sh.WriteText(row, 1, 'background red'); + fmt.SetBackgroundColor(scRed); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcEndsWith, 'g', fmtIdx); + + // conditional format #6: text contains 'ef' + inc(row); + sh.WriteText(row, 0, 'text contains "ef"'); + sh.WriteText(row, 1, 'background red'); + fmt.SetBackgroundColor(scRed); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcContainsText, 'ef', fmtIdx); + + // conditional format #6: text does NOT contain 'ef' + inc(row); + sh.WriteText(row, 0, 'text does not contain "ef"'); + sh.WriteText(row, 1, 'background red'); + fmt.SetBackgroundColor(scRed); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcNotContainsText, 'ef', fmtIdx); + + // conditional format #6: contains error + inc(row); + sh.WriteText(row, 0, 'contains error'); + sh.WriteText(row, 1, 'background red'); + fmt.SetBackgroundColor(scRed); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcContainsErrors, fmtIdx); + + // conditional format #6: no errors + inc(row); + sh.WriteText(row, 0, 'no errors'); + sh.WriteText(row, 1, 'background red'); + fmt.SetBackgroundColor(scRed); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcNotContainsErrors, fmtIdx); + + (* + + sh.Wri + +' { ------ 1st conditional format : cfcEqual ------------------------------- } sh.WriteNumber(0, 0, 1.0); @@ -40,7 +293,6 @@ begin // Use the format as conditional format of A1:A6 when cells are equal to 3. sh.WriteConditionalCellFormat(Range(0, 0, 5, 0), cfcEqual, 3.0, fmtIdx); - { ------- 2nd conditional format : cfcBelowEqualAverage ------------------ } sh.WriteNumber(0, 2, 10.0); sh.WriteNumber(1, 2, 20.0); @@ -85,7 +337,6 @@ begin fmtIdx := wb.AddCellFormat(fmt); sh.WriteConditionalCellFormat(Range(0, 0, 100, 100), cfcContainsErrors, fmtIdx); - { ------ 6th conditional format: unique/duplicate values ----------------- } sh.WriteNumber(0, 1, 1.0); sh.WriteNumber(1, 1, 99.0); @@ -94,6 +345,7 @@ begin sh.WriteConditionalCellFormat(Range(0, 0, 1, 1), cfcUnique, wb.AddCellFormat(fmt)); fmt.SetBackgroundColor(scGreen); sh.WriteConditionalCellFormat(Range(0, 0, 1, 1), cfcDuplicate, wb.AddCellFormat(fmt)); + *) { ------ Save workbook to file-------------------------------------------- } wb.WriteToFile('test.xlsx', true); diff --git a/components/fpspreadsheet/source/common/fpsconditionalformat.pas b/components/fpspreadsheet/source/common/fpsconditionalformat.pas index 31b25cf9e..c6e3a6ed8 100644 --- a/components/fpspreadsheet/source/common/fpsconditionalformat.pas +++ b/components/fpspreadsheet/source/common/fpsconditionalformat.pas @@ -19,18 +19,13 @@ type cfcGreaterThan, cfcLessThan, cfcGreaterEqual, cfcLessEqual, cfcBetween, cfcNotBetween, cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage, + cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, cfcDuplicate, cfcUnique, cfcBeginsWith, cfcEndsWith, cfcContainsText, cfcNotContainsText, cfcContainsErrors, cfcNotContainsErrors ); - {cellIs - expression - colorScale, dataBar, iconSet - containsText, notContainsText, beginsWith, endsWith, containsBlanks, notContainsBlanks, containsErrors, notContainsErrors - } - TsCFCellRule = class(TsCFRule) public Condition: TsCFCondition; diff --git a/components/fpspreadsheet/source/common/fpstypes.pas b/components/fpspreadsheet/source/common/fpstypes.pas index c7fefa84c..d8f00cfc1 100644 --- a/components/fpspreadsheet/source/common/fpstypes.pas +++ b/components/fpspreadsheet/source/common/fpstypes.pas @@ -721,6 +721,7 @@ type // next two are deprecated... NumberFormat: TsNumberFormat; NumberFormatStr: String; + procedure SetBackground(AFillStyle: TsFillStyle; AFgColor, ABgColor: TsColor); procedure SetBackgroundColor(AColor: TsColor); procedure SetBorders(ABorders: TsCellBorders; AColor: TsColor = scBlack; ALineStyle: TsLineStyle = lsThin); @@ -1067,12 +1068,18 @@ end; { TsCellFormat } -procedure TsCellFormat.SetBackgroundColor(AColor: TsColor); +procedure TsCellFormat.SetBackground(AFillStyle: TsFillStyle; + AFgColor, ABgColor: TsColor); begin UsedFormattingFields := UsedFormattingFields + [uffBackground]; - Background.FgColor := AColor; - Background.BgColor := AColor; - Background.Style := fsSolidFill; + Background.FgColor := AFgColor; + Background.BgColor := ABgColor; + Background.Style := AFillStyle; +end; + +procedure TsCellFormat.SetBackgroundColor(AColor: TsColor); +begin + SetBackground(fsSolidFill, AColor, AColor); end; procedure TsCellFormat.SetBorders(ABorders: TsCellBorders; @@ -1082,7 +1089,7 @@ var begin for cb in ABorders do begin - if (AColor = scNone) or (AColor = scTransparent) then + if (AColor = scTransparent) then Exclude(Border, cb) else begin diff --git a/components/fpspreadsheet/source/common/xlsxooxml.pas b/components/fpspreadsheet/source/common/xlsxooxml.pas index 8181fd210..959b0a28d 100644 --- a/components/fpspreadsheet/source/common/xlsxooxml.pas +++ b/components/fpspreadsheet/source/common/xlsxooxml.pas @@ -3343,12 +3343,34 @@ end; procedure TsSpreadOOXMLWriter.WriteConditionalFormatCellRule(AStream: TStream; ARule: TsCFCellRule; ARange: TsCellRange; APriority: Integer); const + TYPE_NAMES: array[TsCFCondition] of String = ( + 'cellIs', 'cellIs', // cfcEqual, cfcNotEqual, + 'cellIs', 'cellIs', 'cellIs', 'cellIs', // cfcGreaterThan, cfcLessThan, cfcGreaterEqual, cfcLessEqual, + 'cellIs', 'cellIs', // cfcBetween, cfcNotBetween, + 'aboveAverage', 'aboveAverage', 'aboveAverage', 'aboveAverage', // cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage, + 'top10', 'top10', 'top10', 'top10', // cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, + 'duplicateValues', 'uniqueValues', // cfcDuplicate, cfcUnique, + 'beginsWith', 'endsWith', // cfcBeginsWith, cfcEndsWith, + 'containsText', 'notContainsText', // cfcContainsText, cfcNotContainsText, + 'containsErrors', 'notContainsErrors' // cfcContainsErrors, cfcNotContainsErrors + ); + OPERATOR_NAMES: array[TsCFCondition] of string = ( + 'equal', 'notEqual', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual', + 'between', 'notBetween', + '', '', '', '', // cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage, + '', '', '', '', // cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, + '', '', // cfcDuplicate, cfcUnique, + '', '', '', 'notContains', //cfcBeginsWith, cfcEndsWith, cfcContainsText, cfcNotContainsText, + '', '' // cfcContainsErrors, cfcNotContainsErrors + ); +{ OPERATOR_NAMES_1: array[cfcEqual..cfcLessEqual] of String = ('equal', 'notEqual', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual'); OPERATOR_NAMES_2: array[cfcBetween..cfcNotBetween] of String = ('between', 'notBetween'); - OPERATOR_NAMES_Text: array[cfcBeginsWith..cfcNotContainsErrors] of String = + TYPE_NAMES: array[cfcBeginsWith..cfcNotContainsErrors] of String = ('beginsWith', 'endsWith', 'containsText', 'notContainsText', 'containsErrors', 'notContainsErrors'); + } FORMULA: array[cfcBeginsWith..cfcNotContainsErrors] of String = ( 'LEFT(%0:s,LEN("%1:s"))="%1:s"', // cfcBeginsWith 'RIGHT(%0:s,Len("%1:s"))="%1:s"', // cfcEndsWidth @@ -3360,8 +3382,9 @@ const var i: Integer; dxfID: Integer; - typeStr, aveStr, stdDevStr, eqAveStr, opStr: String; + typeStr, opStr, formula1Str, formula2Str, param1Str, param2Str, param3Str: String; firstCellOfRange: String; + s: String; begin dxfID := -1; for i := 0 to High(FDifferentialFormatIndexList) do @@ -3371,6 +3394,71 @@ begin break; end; + typeStr := TYPE_NAMES[ARule.Condition]; + if OPERATOR_NAMES[ARule.Condition] = '' then + opStr := '' + else + opStr := ' operator="' + OPERATOR_NAMES[ARule.Condition] + '"'; + formula1Str := ''; + formula2Str := ''; + param1Str := ''; + param2Str := ''; + param3Str := ''; + case ARule.Condition of + cfcEqual..cfcNotBetween: + begin + formula1Str := Format('%s', [ARule.Operand1]); + if (ARule.Condition in [cfcBetween, cfcNotBetween]) then + formula2Str := Format('%s',[ ARule.Operand2]); + end; + cfcAboveAverage..cfcBelowEqualAverage: + begin + if (ARule.Condition in [cfcBelowAverage, cfcBelowEqualAverage]) then + param1Str := ' aboveAverage="0"'; + if (ARule.Condition in [cfcAboveEqualAverage, cfcBelowEqualAverage]) then + param2Str := ' equalAverage="1"'; + if not ((ARule.Operand1 = varNull) or (ARule.Operand1 = 0)) then + param3Str := Format(' stdDev="%d"', [ARule.Operand1]); + end; + cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent: + begin + // // = bottom 30 percent + if ARule.Condition in [cfcBottom, cfcBottomPercent] then + param1Str := ' bottom="1"'; + if ARule.Condition in [cfcTopPercent, cfcBottomPercent] then + param2Str := ' percent="1"'; + param3Str := ' rank="' + VarToStr(ARule.Operand1) + '"'; + end; + cfcDuplicate, cfcUnique: + ; + cfcBeginsWith..cfcNotContainsErrors: + begin + firstCellOfRange := GetCellString(ARange.Row1, ARange.Col1); + formula1Str := + '' + + Format(FORMULA[ARule.Condition], [firstcellOfRange, ARule.Operand1]) + + ''; + param1Str := ' text="' + VarToStr(ARule.Operand1) + '"'; + end; + else + FWorkbook.AddErrorMsg('ConditionalFormat operator not supported.'); + end; + + if formula1Str = '' then + s := Format( + '', [ + typeStr, dxfId, APriority, opStr, param1Str, param2Str, param3Str + ]) + else + s := Format( + '' + + '%s%s' + + '', [ + typeStr, dxfId, APriority, opStr, param1Str, param2Str, param3Str, + formula1Str, formula2Str + ]); + AppendToStream(AStream, s); +(* case ARule.Condition of cfcEqual..cfcLessEqual: AppendToStream(AStream, Format( @@ -3408,6 +3496,21 @@ begin [dxfId, APriority, aveStr, stdDevStr, eqAveStr])); end; + cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent: + begin + if ARole.Condition in [cfcBottom, cfcBottomPercent] then + bottomStr := ' bottom="1"' + else + bottomStr := ''; + if ARole.Condition in [cfcTopPercent, cfcBottomPercent] then + percentStr := ' percent="1" + else + percentStr := ''; + AppendToStream(AStream, Format( + '', + [dxfID, APriority, bottomStr, percentStr, ARole.Operand1])); + end; + cfcBeginsWith..cfcNotContainsErrors: begin firstCellOfRange := GetCellString(ARange.Row1, ARange.Col1); @@ -3437,6 +3540,7 @@ begin else FWorkbook.AddErrorMsg('ConditionalFormat operator not supported.'); end; +*) end; procedure TsSpreadOOXMLWriter.WriteConditionalFormatRule(AStream: TStream;