Simple Bar Chart Graph on Canvas Gadget

Share your advanced PureBasic knowledge/code with the community.
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Simple Bar Chart Graph on Canvas Gadget

Post 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
Last edited by IdeasVacuum on Wed Jun 27, 2012 3:13 pm, edited 1 time in total.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
falsam
Enthusiast
Enthusiast
Posts: 630
Joined: Wed Sep 21, 2011 9:11 am
Location: France
Contact:

Re: Simple Bar Chart Graph on Canvas Gadget

Post by falsam »

Thank you for this fine example :)

➽ Windows 11 64-bit - PB 6.0 x64 - AMD Ryzen 7 - NVIDIA GeForce GTX 1650 Ti

Sorry for my bad english and the Dunning–Kruger effect.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Simple Bar Chart Graph on Canvas Gadget

Post by Kwai chang caine »

Nice effect.
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
Zebuddi123
Enthusiast
Enthusiast
Posts: 794
Joined: Wed Feb 01, 2012 3:30 pm
Location: Nottinghamshire UK
Contact:

Re: Simple Bar Chart Graph on Canvas Gadget

Post by Zebuddi123 »

Thanks Ideasvacuum very nice :) :)

Zebuddi.
malleo, caput, bang. Ego, comprehendunt in tempore
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Simple Bar Chart Graph on Canvas Gadget

Post by IdeasVacuum »

Added a check at line 151 to ensure the highest-score bar does not fall of the chart! :oops:
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
jassing
Addict
Addict
Posts: 1745
Joined: Wed Feb 17, 2010 12:00 am

Re: Simple Bar Chart Graph on Canvas Gadget

Post 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....
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Simple Bar Chart Graph on Canvas Gadget

Post 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).
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
WilliamL
Addict
Addict
Posts: 1214
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: Simple Bar Chart Graph on Canvas Gadget

Post 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.
MacBook Pro-M1 (2021), Sonoma 14.3.1 (CLT 15.3), PB 6.10b7 M1
Polo
Addict
Addict
Posts: 2422
Joined: Tue May 06, 2003 5:07 pm
Location: UK

Re: Simple Bar Chart Graph on Canvas Gadget

Post 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
WilliamL
Addict
Addict
Posts: 1214
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: Simple Bar Chart Graph on Canvas Gadget

Post 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.
MacBook Pro-M1 (2021), Sonoma 14.3.1 (CLT 15.3), PB 6.10b7 M1
Polo
Addict
Addict
Posts: 2422
Joined: Tue May 06, 2003 5:07 pm
Location: UK

Re: Simple Bar Chart Graph on Canvas Gadget

Post 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 )
User avatar
Guimauve
Enthusiast
Enthusiast
Posts: 742
Joined: Wed Oct 22, 2003 2:51 am
Location: Canada

Re: Simple Bar Chart Graph on Canvas Gadget

Post 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
Dear Optimist, Pessimist,
and Realist,

While you guys were
busy arguing about the
glass of water, I DRANK IT !

Sincerely,
the Opportunist
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Simple Bar Chart Graph on Canvas Gadget

Post by IdeasVacuum »

Hi Guimauve, do you have a set of values that I can test with?
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Guimauve
Enthusiast
Enthusiast
Posts: 742
Joined: Wed Oct 22, 2003 2:51 am
Location: Canada

Re: Simple Bar Chart Graph on Canvas Gadget

Post 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
Dear Optimist, Pessimist,
and Realist,

While you guys were
busy arguing about the
glass of water, I DRANK IT !

Sincerely,
the Opportunist
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Simple Bar Chart Graph on Canvas Gadget

Post 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
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
Post Reply