PIE() Example Project of Clarion's Draw a Pie Chart

On ClarionLive AI Friday Roberto Renz showed a Template he made for Pie Charts. I didn’t know Clarion had a PIE() command so was curious what it could do, and how it looked, so I made a simple example:

PieTestRecording

PIE() is not a Window Control, it “Draws a Pie Chart on the current window or report.”

PIE(X,Y, Width,Height, Slices[], Colors[], Depth, WholeValue, StartAngle, attributelist)

!!! <summary>          Draws a pie chart on the current window or report.
!!! <param name="x">          An integer expression that specifies the horizontal position of the starting point.</param>
!!! <param name="y">          An integer expression that specifies the vertical position of the starting point.</param>
!!! <param name="width">      An integer expression that specifies the width.</param>      
!!! <param name="height">     An integer expression that specifies the height.</param>
!!! <param name="slices">     A SIGNED array of values that specify the relative size of each slice of the pie.</param>
!!! <param name="colors">     A LONG array that specifies the fill color for each slice.</param>
!!! <param name="depth">      An integer expression that specifies the depth of the three-dimensional pie chart. If omitted, the chart is two-dimensional.</param>
!!! <param name="WholeValue"> A numeric constant or variable that specifies the total value required to create a complete pie chart. If omitted, the sum of the slices array is used.</param>
!!! <param name="StartAngle"> A numeric constant or variable that specifies the starting point of the first slice of the pie, measured as a fraction of the wholevalue. If omitted (or zero), the first slice starts at the twelve o'clock position.</param>
!!! <param name="Attribs">    A string constant, variable, or EQUATE containing an optional type of output document and its associated attributes. Only valid when the target is a REPORT.</param>

PieTest_Porject_20260622_133848.ZIP (12.5 KB)

Most of the source code to see how it works without download

QuickPieMain   PROCEDURE
MaxSlices        EQUATE( 5 )
SliceValue  LONG,DIM(MaxSlices)
SliceColor  LONG,DIM(MaxSlices)
SliceTotal  LONG
PieDepth        LONG(20)
PieEmptyPct     LONG
PieWholeValue   SIGNED      !Usually Total but may be adjusted by Empty Pct
PieAnglePct     LONG        !This could be degrees 0-360 and then % = Angle/360 * 100
PieAngleValue   LONG        !Whole Value x % Angle
PieIsElipse     BOOL        !False makes it Circle, but Elipse can have a nice look
AutoRefresh     BOOL(1)     !Every change instantly refreshed
Ndx             LONG 

Window WINDOW('Quick Pie'),AT(,,350,200),GRAY,IMM,SYSTEM,FONT('Segoe UI',10),RESIZE
        ENTRY(@n11),AT(45,25,55),USE(SliceValue[1]),RIGHT
        ENTRY(@n11),AT(45,40,55),USE(SliceValue[2]),RIGHT
        ENTRY(@n11),AT(45,55,55),USE(SliceValue[3]),RIGHT
        ENTRY(@n11),AT(45,70,55),USE(SliceValue[4]),RIGHT
        ENTRY(@n11),AT(45,85,55),USE(SliceValue[5]),RIGHT
        ENTRY(@n11),AT(45,100,55),USE(SliceTotal),SKIP,RIGHT,COLOR(COLOR:BTNFACE),TIP('Total'),READONLY
        BOX,AT(8,25,12,12),USE(?SliceColorBox_1),COLOR(COLOR:Black),FILL(0616FFFh),ROUND,LINEWIDTH(1)
        BOX,AT(8,40,12,12),USE(?SliceColorBox_2),COLOR(COLOR:Black),FILL(0808000h),ROUND,LINEWIDTH(1)
        BOX,AT(8,55,12,12),USE(?SliceColorBox_3),COLOR(COLOR:Black),FILL(007C1FFh),ROUND,LINEWIDTH(1)
        BOX,AT(8,70,12,12),USE(?SliceColorBox_4),COLOR(COLOR:Black),FILL(0E16941h),ROUND,LINEWIDTH(1)
        BOX,AT(8,85,12,12),USE(?SliceColorBox_5),COLOR(COLOR:Black),FILL(08515C7h),ROUND,LINEWIDTH(1)
        STRING('1.'),AT(29,25),USE(?Prompt_Pie_1)
        STRING('2.'),AT(29,40),USE(?Prompt_Pie_2)
        STRING('3.'),AT(29,55),USE(?Prompt_Pie_3)
        STRING('4.'),AT(29,70),USE(?Prompt_Pie_4)
        STRING('5.'),AT(29,85),USE(?Prompt_Pie_5)
        PROMPT('Empty %:'),AT(11,119),USE(?PieEmptyPct:Prompt1)
        SPIN(@n3~%~),AT(45,118,55,11),USE(PieEmptyPct),HVSCROLL,TIP('Percent of Pie that is Empty, n' & |
                'ormally 0%<13,10>This calculates a Whole Value > 100% of the Total:<13,10>WholeValu' & |
                'e = SliceTotal / (1 - Pie Empty%/100)<13,10>This Whole Value passed to PIE() create' & |
                's the Empty Slice'),RANGE(0,99)
        STRING(@n11b),AT(44,130,56,10),USE(PieWholeValue),RIGHT(2)
        PROMPT('Angle %:'),AT(12,144),USE(?PieAnglePct:Prompt1)
        SPIN(@n3~%~),AT(45,144,55,11),USE(PieAnglePct),HVSCROLL,TIP('Starting point of the first sli' & |
                'ce of the pie, measured as a fraction of the wholevalue.<13,10>If omitted (or zero)' & |
                ', the first slice starts at the twelve o''clock position.'),RANGE(0,99)
        STRING(@n11b),AT(44,156,56,10),USE(PieAngleValue),RIGHT(2)
        PROMPT('3D Depth:'),AT(7,172),USE(?PieDepth:Prompt1)
        SPIN(@n2),AT(45,172,55,11),USE(PieDepth),HVSCROLL,TIP('The depth of the three-dimensional pi' & |
                'e chart<13,10>If omitted or zero the chart is two-dimensional'),RANGE(0,99)
        CHECK('Elipse Pie'),AT(45,187),USE(PieIsElipse),TIP('Check for Elipse that can have a diffen' & |
                't 3D look')
        BUTTON('&Refresh'),AT(5,4,40),USE(?RefreshBtn),TIP('Refresh Pie to draw with current parameters')
        CHECK('Auto'),AT(49,7),USE(AutoRefresh),SKIP,FONT(,8),TIP('Auto Refresh on all changes')
        IMAGE,AT(110,15),FULL,USE(?PieImage1)
        TEXT,AT(110,3,,10),FULL,USE(Glo:PieCode),SKIP,FLAT,FONT('Consolas',10),COLOR(COLOR:BTNFACE), |
                TIP('PIE( X,Y, Width,Height, <13,10>Slices[], Colors[], <13,10>Depth, WholeValue, ' & |
                '<13,10>StartAngle)'),READONLY,SINGLE
        BUTTON('Rando&m'),AT(5,101,36,11),USE(?RandomBtn),FONT(,8),TIP('Random 5 Slice Values')
        BUTTON('Slice'),AT(4,186,22,11),USE(?SliceBtn),FONT(,8),TIP('Random Slice of Pie')
        ELLIPSE,AT(114,19,177,177),USE(?EllipseForSizing_HIDE),COLOR(COLOR:Black),FILL(0C0E0FFH),HIDE, |
                LINEWIDTH(1)
    END
    CODE
    OPEN(WINDOW)
    0{PROP:text}=clip(0{PROP:text}) &' - Library ' & system{PROP:LibVersion,2} &'.'& system{PROP:LibVersion,3}
    LOOP Ndx=1 TO MaxSlices
         SliceValue[Ndx] = CHOOSE(Ndx,300,600,750,900,1050)      !Total 3600 for 10 x 360 degrees
         SliceColor[Ndx] = (?SliceColorBox_1-1+Ndx){PROP:Fill}   !Design colors on Window... its just a test
    END 
    DO SliceTotalRtn
    0{PROP:MinWidth}=0{PROP:Width} * .75 ; 0{PROP:MinHeight}=0{PROP:Height} * .75
    0{PROP:MaxWidth}=0{PROP:Width} * 2.0 ; 0{PROP:MaxHeight}=0{PROP:Height} * 2.0   !Make easy to no go too big
    ACCEPT
        CASE EVENT()
        OF EVENT:OpenWindow ; POST(EVENT:Accepted,?RefreshBtn)
        OF EVENT:Sized      ; IF AutoRefresh THEN POST(EVENT:DoResize,,,1).
        OF EVENT:DoResize   ; IF AutoRefresh THEN DO PieRefreshRtn.
        END
        CASE ACCEPTED()
        OF ?SliceValue_1 TO ?SliceValue_5   ; DO SliceTotalRtn ; DISPLAY(?SliceTotal) 
        OF ?RefreshBtn    ; DO PieRefreshRtn
        OF ?RandomBtn     ; LOOP Ndx=1 TO MaxSlices ; SliceValue[Ndx] = RANDOM(100,999)*10 ; END
                            DO SliceTotalRtn ; SliceValue[MaxSlices] += (100 - SliceTotal % 100)  !Total in 100's
                            DO SliceTotalRtn ; DO PieRefreshRtn
        
        OF ?SliceBtn     ; CLEAR(SliceValue[]) ; SliceValue[Random(1,MaxSlices)]=1000 ; DO SliceTotalRtn
                           PieEmptyPct=RANDOM(66,95) 
                           PieAnglePct=RANDOM(0,99)
        END
        IF AutoRefresh THEN
           CASE EVENT()
           OF EVENT:Accepted OROF EVENT:NewSelection ; DO PieRefreshRtn
           END 
        END 
    END
    CLOSE(WINDOW)
!----------------------------
SliceTotalRtn ROUTINE
    SliceTotal = 0
    LOOP Ndx=1 TO MaxSlices ; SliceTotal += SliceValue[Ndx] ; END 
    EXIT

PieRefreshRtn ROUTINE 
    DATA
PieW SIGNED 
PieH SIGNED      
    CODE
    DO SliceTotalRtn     
    IF PieEmptyPct >= 100 THEN PieEmptyPct=0 ; DISPLAY.
    IF ~PieEmptyPct THEN 
       PieWholeValue = SliceTotal 
       ?PieWholeValue{PROP:Tip}='0% Empty so equals Slice Total '& SliceTotal
    ELSE
!Help: Supplying a WholeValue parameter that is greater than the sum of all the slices array elements creates a pie chart with a piece missing.
       PieWholeValue = SliceTotal / ((100-PieEmptyPct) / 100)
       ?PieWholeValue{PROP:Tip}=PieEmptyPct & '% Empty' & |
                                '<13,10>= Slice Total / (1 - Empty%)' & |
                                '<13,10>= '& SliceTotal & ' / (1 - '& PieEmptyPct/100 &')' & |
                                '<13,10>'& PieEmptyPct & '% Empty = '& INT(360 * PieEmptyPct/100) &' Degrees'                                
    END 

    IF PieAnglePct >= 100 THEN PieAnglePct=0 ; DISPLAY.
    IF ~PieAnglePct THEN 
        PieAngleValue = 0 
        ?PieAngleValue{PROP:Tip}='0% Angle so Zero Angle Value'
    ELSE
!Help: The slices of the pie are created clockwise from the StartAngle parameter as a fraction of the WholeValue.
       PieAngleValue = PieWholeValue * (PieAnglePct/100) 
       ?PieAngleValue{PROP:Tip}=PieAnglePct & '% Angle<13,10>= Whole Value * Angle %' & |
                               '<13,10>= '& PieWholeValue & ' * '& PieAnglePct/100 & |
                               '<13,10>'& PieAnglePct &'% Angle = '& INT(360 * PieAnglePct/100)  &' Degrees'
    END

    GETPOSITION(?PieImage1,,,PieW,PieH)   
    IF ~PieIsElipse THEN       !If its Not an Ellipse
       IF PieH > PieW THEN     ! must be H = W i.e. a Square
          PieH = PieW
       ELSE 
          PieW = PieH
       END 
    END 
    myPieDraw(Window, ?PieImage1,SliceValue[],SliceColor[], PieW, PieH,PieDepth,PieWholeValue,PieAngleValue) !, Color:None ) !,LONG pBackColor=COLOR:White)    
  ! Show PIE Source Code above Chart in Glo:PieCode TEXT contro 
    IF PieEmptyPct THEN Glo:PieCode=CLIP(Glo:PieCode) &' Total='& SliceTotal &' Empty='& PieEmptyPct &'%'.
    IF PieAnglePct THEN Glo:PieCode=CLIP(Glo:PieCode) &' Angle='& PieAnglePct &'%'.

myPieDraw  PROCEDURE(WINDOW pWnd, SIGNED pImageFeq,*SIGNED[] pSlices,*LONG[] pColors,SIGNED pPieW,SIGNED pPieH,SIGNED pDepth=0, |
                     SIGNED pWholeValue=0,SIGNED StartAngle=0,LONG pBackColor=COLOR:White)
Indent LONG,AUTO  !Indent to leave margins
  CODE                       !From robertorenz templatemaker myPie 
  SETTARGET(pWnd,pImageFeq)  ! Draw into the IMAGE control, Must have (Window,). Makes (X,Y) relative to Image Box
  BLANK()                    ! WIPE all prior graphics within the SetTarget(Wnd,Image) (no resize artifacts). FYI can BLANK(x,y,w,h)
  IF pBackColor <> COLOR:None THEN      !Carl Allow COLOR:None to omit BOX
     SETPENCOLOR(pBackColor)            ! paint the chosen background  color
     BOX(0,0,pImageFeq{PROP:Width},pImageFeq{PROP:Height},pBackColor)
  END 
  SETPENCOLOR(COLOR:Black)                                    ! slice outlines
  Indent = CHOOSE(pPieW<pPieH,pPieW,pPieH) * .025 !Carl added a margin of 2.5%. This could a parameter of the call
  pPieW -= Indent * 2
  pPieH -= Indent * 2
  PIE(Indent,Indent, pPieW,pPieH, pSlices[],pColors[],pDepth,pWholeValue,StartAngle) 
  SETTARGET()  
! Show PIE Source Code above Chart in Glo:PieCode TEXT control 
  Glo:PieCode='PIE('& Indent &','& Indent &','& pPieW &','& pPieH &',Value[],Color[],'& pDepth &','& pWholeValue &','& StartAngle &')'
  RETURN 

Amazing! Unfortunately standard PIE doesn’t support darken bottom part in 3D mode, or did I miss something?

It does not support antialiasing either that I know of, but we can still have fun with the old tools in the shed.

One thing I learned is you can SetTarget() to an IMAGE control which limits Graphics Draw commands to that rectangle. E.g. a BLANK command only erases within that Image boundry. It makes all the Draw (X,Y) relative to the Image AT(X,Y) i.e. BOX(0,0) draws at the Image (X,Y).

For that to work it must be SetTarget(Window, ?Image) or SetTarget(Report, ?Image). It does not work as SetTarget(, ?Image) without the Target Window/Report.

You can also SetTarget(Report, ?BandFEQ) which I used with SetPenStyle() and LINE() for Dashed or Dotted lines on a Report.

The Help since C4 no longer splits out the Graphics procedures into their own section, now they are mixed in with all the commands which makes it hard to quickly grasp them all. I think this is the list:

 BLANK    (<SIGNED x>, <SIGNED y>, <SIGNED width>, <SIGNED height>),NAME('Cla$BLANK')

 ARC      (SIGNED x, SIGNED y, SIGNED width, SIGNED height, SIGNED startAngle, SIGNED endAngle, <STRING attributeList>),NAME('Cla$ARC')
 BOX      (SIGNED x, SIGNED y, SIGNED width, SIGNED height, LONG fill=COLOR:None, <STRING attributeList>),NAME('Cla$BOX')
 CHORD    (SIGNED x, SIGNED y, SIGNED width, SIGNED height, SIGNED startAngle, SIGNED endAngle, LONG fill=COLOR:None, <STRING attributeList>),NAME('Cla$CHORD')
 ELLIPSE  (SIGNED x, SIGNED y, SIGNED width, SIGNED height, LONG fill=COLOR:None, <STRING attributeList>),NAME('Cla$ELLIPSE')
 IMAGE    (SIGNED x, SIGNED y, SIGNED width=0, SIGNED height=0, STRING fileName, <STRING attributeList>),NAME('Cla$IMAGE')
 LINE     (SIGNED x, SIGNED y, SIGNED width, SIGNED height, <STRING attributeList>),NAME('Cla$LINE')
 PIE      (SIGNED x, SIGNED y, SIGNED width, SIGNED height, *SIGNED[] slices, *LONG[] colors, SIGNED depth=0, UNSIGNED wholeValue=0, SIGNED startAngle=0, <STRING attributeList>),NAME('Cla$PIE')
 POLYGON  (*SIGNED[] XY_Array, LONG fill=COLOR:None, <STRING attributeList>),NAME('Cla$POLYGON')
 ROUNDBOX (SIGNED x, SIGNED y, SIGNED width, SIGNED height, LONG fill=COLOR:None, <STRING attributeList>),NAME('Cla$ROUNDBOX')
 SHOW     (SIGNED x, SIGNED y, STRING str, <STRING attributeList>),NAME('Cla$SHOW')  !The font used is the current font for the window or report.
 TYPE     (STRING str),NAME('Cla$StackTYPE')    !Writes a string to the current window or report.
      
 PENCOLOR(),SIGNED,NAME('Cla$PENCOLOR')
 PENWIDTH(),SIGNED,NAME('Cla$PENWIDTH')
 PENSTYLE(),SIGNED,NAME('Cla$PENSTYLE')
 SETPENWIDTH(SIGNED width=0),NAME('Cla$SETPENWIDTH')
 SETPENSTYLE(SIGNED style=PEN:Solid),NAME('Cla$SETPENSTYLE')
 SETPENCOLOR(SIGNED color=COLOR:WindowText),NAME('Cla$SETPENCOLOR')
 SETTARGET(<WINDOW target>, UNSIGNED band=0),NAME('Cla$SETTARGET') !The control number or field equate label of the REPORT band (or IMAGE control in a window target) to draw graphics primitives to (ARC, CHORD, etc.)     

For writing Text the SHOW() and TYPE() commands seem lame that they write using the Window Font. I did a quick test of changing that font and it did not affect Show.

For Text what I would do is CREATE() String controls so you can set Font and every attribute. To allow a way to Blank() use a Group you CREATE() and make that the Parent control CREATE(,Create:String,GroupFEQ). Then if you DESTROY(GroupFEQ) it also destroys all the Child controls.

If you have the Enterprise Edition that includes the SV Graphics library that also offers PIE and many more. The source for GraphClass is in LibSrc SvGraph.INC and .CLW. There are Templates (SvGraph.TPL and .TPW) and it is documented in the Help.

The source shows it uses the PIE() command in the RTL. I have not tried it, the Help screen captures show the same non-shaded 3D PIE as my example.

This would be the way to do something for production as it offers things like Legends, Axis, Scales, etc.

Going to an IMAGE() invites the saving of that image as well. :slight_smile:

I tried ?PieImage{PROP:ClipBits}=1 and got nothing on the Clipboard. I did not try the others that move to MEMO or BLOB.