Page 1 of 2

Simple Bar Chart Graph on Canvas Gadget

Posted: Tue Jun 26, 2012 12:40 pm
by IdeasVacuum
Crude, small and simple example, easy to butcher for your own use, or at least learn how not to do it :mrgreen:

The chart grid does change to suit the data, up to a point, plus the Canvas size is related to the Window (fixed) Size. If the range of data you wish to cater for is large, plonk the canvas on a ScrollGadget and have your code dynamically change the size of both gadgets as required.

Code: Select all

;Simple Bar Chart Graph. IdeasVacuum. Free to use, no restrictions.

Enumeration
#WinGraph
#CvsGraph
#FileIO
EndEnumeration

Structure Player
sPlayerName.s
iScore.i
iNameWidth.i
EndStructure

NewList Scores.Player()

Global  igHighestScore.i = 0
Global   igLongestName.i = 0
Global       igWindowW.i = 700
Global       igWindowH.i = 500
Global       igCanvasW.i = (igWindowW - 20)
Global       igCanvasH.i = (igWindowH - 20)
Global igWinBackColour.i = RGB(224,223,227) ;Classic Windows back colour

Global      igFontID01.i = LoadFont(1, "Arial", 8, #PB_Font_Bold | #PB_Font_HighQuality + 2)
Global      igFontID02.i = LoadFont(2, "Arial", 10, #PB_Font_Bold | #PB_Font_HighQuality + 2)

Macro RoundTo(i, s)
;-----------------
           ((s) * Round((i) / (s) + 0.5, #PB_Round_Down))
EndMacro

Procedure.i LoadInfo()
;--------------------
Shared Scores.Player()
Protected        sVal.s = ""
Protected sScoresFile.s = "C:\MyGame\HighestScores.txt" ;<<-- Insert your own folder/file here

;Load the Player Data
;At the same time, get the Name length for right-alignment on graph later

If ReadFile(#FileIO,sScoresFile)

        StartDrawing(CanvasOutput(#CvsGraph)) ;Needed for TextWidth function (nothing is drawn yet)
         DrawingFont(igFontID02)

      While Not Eof(#FileIO)

            sVal = ReadString(#FileIO)

            AddElement(Scores.Player())
            Scores.Player()\sPlayerName = StringField(sVal, 1, Chr(44))    ;delimiter is a comma
                 Scores.Player()\iScore = ValD(StringField(sVal, 2, Chr(44)))
             Scores.Player()\iNameWidth = TextWidth(Scores.Player()\sPlayerName)

            If(Scores.Player()\iScore > igHighestScore) : igHighestScore = Scores.Player()\iScore : EndIf
            If(Scores.Player()\iNameWidth > igLongestName) : igLongestName = Scores.Player()\iNameWidth : EndIf
      Wend

          StopDrawing()
            CloseFile(#FileIO)

      ProcedureReturn(#True)
Else
      MessageRequester("Problem","Could not open HighestScores.txt file",#PB_MessageRequester_Ok | #MB_ICONERROR)
       ProcedureReturn(#False)
EndIf

EndProcedure

Procedure DrawBarChart()
;-----------------------
;Draw a Chart on a Canvas, as though the Canvas is a sheet of paper
Shared Scores.Player()
Protected   iTotalPlayers.i = 0
Protected         iChartW.i = 0
Protected         iChartH.i = 0
Protected        iChartX0.i = 0
Protected        iChartY0.i = 0
Protected     iGridPitchX.i = 0
Protected              iX.i = 0
Protected       iTextCtrX.i = 0
Protected         iLabelX.i = 0
Protected           iLblX.i = 0
Protected         iColour.i = 0
Protected iPreviousColour.i = 0
Protected          dRatio.d = 0.00
Protected     iGridPitchY.i = 0
Protected              iY.i = 0
Protected              iW.i = 0
Protected          iTextX.i = 0
Protected          iTextY.i = 0
Protected       iTextCtrY.i = 0
Protected   iHighestScore.i = 0
Protected          iMulti.i = 100

     If(igLongestName > 1) ;If it isn't, there was an issue with HighestScores.txt

                 StartDrawing(CanvasOutput(#CvsGraph))
                  DrawingFont(igFontID02)
                    BackColor(RGB(255,255,255))
                  DrawingMode(#PB_2DDrawing_Default)

                ;Draw shadow
                          Box(0,0,igCanvasW,igCanvasH,RGB(136,136,136)) ;Dark Grey
                          Box(-2,-2,igCanvasW + 4,10,igWinBackColour) ;(across top)
                          Box(-2,-2,10,igCanvasH + 4,igWinBackColour) ;(vertical left)
                          Box(0,0,igCanvasW -8,igCanvasH - 8,RGB(255,255,255)) ;White (Chart Region)

                ;Draw Canvas (paper) boundary
                  DrawingMode(#PB_2DDrawing_Outlined)
                          Box(0,0,igCanvasW -8,igCanvasH - 8,RGB(0,0,0)) ;Black

                ;Draw chart
                ;Chart has approx 15pix margin inside Canvas, all-round
                      iChartW = (igCanvasW - 40) - (igLongestName + 10)
                      iChartH = (igCanvasH - 65)

                ;Tweak Chart Height so that bars fit exact integer value (using double val = ugly bars)
                iTotalPlayers = ListSize(Scores.Player())
                  iGridPitchY = (iChartH/iTotalPlayers)
                      iChartH = (iGridPitchY * iTotalPlayers)
                     iChartX0 = (igLongestName + 25)
                     iChartY0 = 15

               ;Tweak Chart Width so that grid lines fit exact integer value (using double val = ugly grid)
                  iGridPitchX = (iChartW/10)
                      iChartW = (iGridPitchX * 10)

                DrawingMode(#PB_2DDrawing_Default)
                        Box(iChartX0,iChartY0,iChartW,iChartH,RGB(230,230,250)) ;Graph Background Lavenda

                ;Chart Grid Vertical Lines
                DrawingMode(#PB_2DDrawing_Default)

                iX = iChartX0

                For i = 0 To 10
                                                           ;X Y        X2  Y2            Colour
                      If(iX < iChartW + iChartX0) : LineXY(iX,iChartY0,iX,(iChartH + 20),RGB(127,127,127)) : EndIf ;Grey Grid
                                                           ;X  Y                   X2  Y2                      Colour
                                                    LineXY(iX,(iChartY0 + iChartH),iX,(iChartY0 + iChartH + 5),RGB(0,0,0)) ;Black 'comb'

                         iX + iGridPitchX
                Next i

               ;Chart Labels along X
               If(igHighestScore > 999) : iMulti = 1000 : EndIf
               ;Determine nearest 100 or 1000
               iHighestScore = RoundTo(igHighestScore, iMulti)
               If(igHighestScore > iHighestScore)

                      iHighestScore = ((igHighestScore - iHighestScore) + (iHighestScore + 20))

               EndIf

                     iLabelX = (iHighestScore/10)
                     iLabelX = RoundTo(iLabelX, 5)
                       iLblX = 0
                          iX = iChartX0

                 For i = 0 To 11

                          iTextCtrX = (TextWidth(Str(iLblX)) / 2)

                          DrawText(iX - iTextCtrX,iChartH + 25,Str(iLblX),RGB(0,0,0))

                             iX + iGridPitchX
                          iLblX + iLabelX
                 Next i

                ;Chart Labels along Y + bars
                    iTextCtrY = (TextHeight("M") /2)
                       iTextY = (iChartY0 + (iGridPitchY/2)) - iTextCtrY
                           iY = iChartY0
                       dRatio = (iChartW/iHighestScore)

                FirstElement(Scores.Player())

                For i = 1 To iTotalPlayers

                                       iTextX = ((iChartX0 - 5) - Scores.Player()\iNameWidth)

                            BackColor(RGB(255,255,255))
                          DrawingFont(igFontID02)
                             DrawText(iTextX,iTextY,Scores.Player()\sPlayerName,RGB(0,0,0))

                                           iW = (dRatio * Scores.Player()\iScore)
                                      iScoreX = ((iChartX0 + iW) - (TextWidth(StrD(Scores.Player()\iScore,0)) + 5))
                                      iColour = RGB(Random(255),Random(255),Random(255))
                                   If(iColour = iPreviousColour) : iColour = RGB(Random(255),Random(255),Random(255)) : EndIf
                            BackColor(iColour)
                          DrawingFont(igFontID01)
                                  Box(iChartX0,iY,iW,iGridPitchY,iColour)
                             DrawText(iScoreX,iTextY,StrD(Scores.Player()\iScore,0),RGB(0,0,0))
                          NextElement(Scores.Player())

                              iPreviousColour = iColour
                                           iY + iGridPitchY
                                       iTextY + iGridPitchY
                Next i

                ;Chart Grid Outline
                DrawingMode(#PB_2DDrawing_Outlined)
                        Box(iChartX0,iChartY0,iChartW,iChartH,RGB(0,0,0)) ;Black

                StopDrawing()
     EndIf

EndProcedure

Procedure ShowWin()
;------------------

      If OpenWindow(#WinGraph, 0, 0, igWindowW, igWindowH, "Highest Scores", #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_ScreenCentered)

                       SetWindowColor(#WinGraph,igWinBackColour)
                         CanvasGadget(#CvsGraph,10,10,igCanvasW,igCanvasH)
                   SetGadgetAttribute(#CvsGraph,#PB_Canvas_Cursor,#PB_Cursor_Hand)
      Else
                     MessageRequester("Problem","Failed to open Window",#PB_MessageRequester_Ok | #MB_ICONERROR)
      EndIf

EndProcedure

Procedure WaitForUser()
;----------------------

Protected iEvent.i = 0

       Repeat

             iEvent = WaitWindowEvent(1)

       Until iEvent = #PB_Event_CloseWindow

EndProcedure

;## MAIN #############
ShowWin()
LoadInfo()
DrawBarChart()
WaitForUser()
;#####################

End
Sample data file HighestScores.txt:
Cheryl,180
An Onymous,122
Beyonce,185
An Other,211
Joe Blogs,132
Will I Am,199
Rihanna,223
Ying Qin,285
Britney,180
John Smith,177
Lady Penelope, 252
Batman,99

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Tue Jun 26, 2012 1:13 pm
by falsam
Thank you for this fine example :)

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Tue Jun 26, 2012 3:06 pm
by Kwai chang caine
Nice effect.
Thanks for sharing 8)

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Tue Jun 26, 2012 5:13 pm
by Zebuddi123
Thanks Ideasvacuum very nice :) :)

Zebuddi.

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Wed Jun 27, 2012 3:16 pm
by IdeasVacuum
Added a check at line 151 to ensure the highest-score bar does not fall of the chart! :oops:

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Sat Sep 15, 2012 12:53 am
by jassing
Nice -- but has some display issues:
Image

It seems that someone could make some money making an easy re-usable graphing objects (bar, pie, graph, 3d, etc) to replace the rmchart....

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Sat Sep 15, 2012 3:35 pm
by IdeasVacuum
....yep, you need to adjust the code to suit your data range. In my actual app everything is variable dynamically, including paper sizes, but it is still specific to purpose. I think the ability to tailor a specific chart in this way is much better than RM, which simply gets too complicated (and that complication has led to bugs which of course are never going to be fixed).

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Sun Sep 16, 2012 6:52 pm
by WilliamL
nice.. works fine here.. no artifacts

I just had to simplify the MessageRequesters and change the path and it ran perfectly on my Mac.

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Sun Sep 16, 2012 7:05 pm
by Polo
Worked on a chart library a year ago - never finished it, when I get time I should get back to it :P

Image

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Sun Sep 16, 2012 7:16 pm
by WilliamL
That's a nice pie chart too, polo! ..and you covered all the basics of living :)

I think we all get around to making some kind of chart sooner or later.

Nice to see some chart code on the forum.

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Sun Sep 16, 2012 7:36 pm
by Polo
WilliamL wrote:That's a nice pie chart too, polo! ..and you covered all the basics of living :)
:lol:

(I actually forgot Purebasic :P )

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Fri Oct 04, 2013 11:51 am
by Guimauve
Hello everyone,

@IdeasVacuum : I'm trying to readapt your code for a job project and so for it's working properly except for small values (i.e. smaller than 100 and smaller than 10).

Normally I'm able to hack others code but not this time perhaps I'm too exhausted right now. Anyway are you willing to update your code ?

Thanks beforehand
Guimauve

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Fri Oct 04, 2013 1:35 pm
by IdeasVacuum
Hi Guimauve, do you have a set of values that I can test with?

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Fri Oct 04, 2013 2:25 pm
by Guimauve
Of course the first set :

Code: Select all

TooShort,3
Correct,8
TooLong,5
A second set :

Code: Select all

TooShort,1
Correct,18
TooLong,25
A third set :

Code: Select all

TooShort,6
Correct,5
TooLong,11
In fact I use your chart to display frequency results (Too Short, Correct, Too long).

Regards
Guimauve

Re: Simple Bar Chart Graph on Canvas Gadget

Posted: Fri Oct 04, 2013 2:58 pm
by IdeasVacuum
Hmm, I think you need a few tweaks to display just the 3 values - the Canvas Height is too big.
To correct the fault with the grid labels, replace line 147:

Code: Select all

;Chart Labels along X
If(igHighestScore > 5)  : iMulti = 5  : EndIf
If(igHighestScore > 10) : iMulti = 10 : EndIf
If(igHighestScore > 15) : iMulti = 15 : EndIf