Xojo Conferences
MBSSep2018MunichDE
XDCMay2019MiamiUSA

Platforms to show: All Mac Windows Linux Cross-Platform

/ChartDirector/finance with ChartDirector Control
Function:
Required plugins for this example: MBS ChartDirector Plugin, MBS Images Plugin
You find this example project in your Plugins Download as a Xojo project file within the examples folder: /ChartDirector/finance with ChartDirector Control
This example is the version from Wed, 13th Dec 2016.
Project "finance with ChartDirector Control.rbp"
Class App Inherits Application
Const kEditClear = "&Löschen"
Const kFileQuit = "Beenden"
Const kFileQuitShortcut = ""
End Class
Class PicWindow Inherits Window
Control ChartDirectorControl1 Inherits ChartDirectorControl
ControlInstance ChartDirectorControl1 Inherits ChartDirectorControl
EventHandler Function Render(ViewPort As CDViewPortManagerMBS, Factor As Double, Width As Integer, Height As Integer) As CDBaseChartMBS // Create a finance chart demo containing 100 days of data const noOfDays = 100 // To compute moving averages starting from the first day, we need to get extra // data points before the first day const extraDays = 30 // In this exammple, we use a random number generator utility to simulate the // data. We set up the random table to create 6 cols x (noOfDays + extraDays) // rows, using 9 as the seed. dim rantable as new CDRanTableMBS(9, 6, noOfDays + extraDays) // Set the 1st col to be the timeStamp, starting from Sep 4, 2002, with each row // representing one day, and counting week days only (jump over Sat and Sun) rantable.setDateCol(0, CDFinanceChartMBS.chartTime(2002, 9, 4), 86400, true) // Set the 2nd, 3rd, 4th and 5th columns to be high, low, open and close data. // The open value starts from 100, and the daily change is random from -5 to 5. rantable.setHLOCCols(1, 100, -5, 5) // Set the 6th column as the vol data from 5 to 25 million rantable.setCol(5, 50000000, 250000000) // Now we read the data from the table into arrays dim timeStamps(-1) as double = rantable.getCol(0).Values dim highData(-1) as double = rantable.getCol(1).Values dim lowData(-1) as double = rantable.getCol(2).Values dim openData(-1) as double = rantable.getCol(3).Values dim closeData(-1) as double = rantable.getCol(4).Values dim volData(-1) as double = rantable.getCol(5).Values // Create a FinanceChart object of width 640 pixels dim c as new CDFinanceChartMBS(640) // Add a title to the chart call c.addTitle("Finance Chart Demonstration") // Set the data into the finance chart object call c.setData(timeStamps, highData, lowData, openData, closeData, volData, extraDays) // Add the main chart with 240 pixels in height call c.addMainChart(240) // Add a 5 period simple moving average to the main chart, using brown color call c.addSimpleMovingAvg(5, &h663300) // Add a 20 period simple moving average to the main chart, using purple color call c.addSimpleMovingAvg(20, &h9900ff) // Add an HLOC symbols to the main chart, using green/red for up/down days call c.addHLOC(&h008000, &hcc0000) // Add 20 days bollinger band to the main chart, using light blue (9999ff) as the // border and semi-transparent blue (c06666ff) as the fill color call c.addBollingerBand(20, 2, &h9999ff, &hc06666ff) // Add a 75 pixels volume bars sub-chart to the bottom of the main chart, using // green/red/grey for up/down/flat days call c.addVolBars(75, &h99ff99, &hff9999, &h808080) // Append a 14-days RSI indicator chart (75 pixels high) after the main chart. // The main RSI line is purple (800080). Set threshold region to +/- 20 (that is, // RSI = 50 +/- 25). The upper/lower threshold regions will be filled with red // (ff0000)/blue (0000ff). call c.addRSI(75, 14, &h800080, 20, &hff0000, &h0000ff) // Append a 12-days momentum indicator chart (75 pixels high) using blue (0000ff) // color. call c.addMomentum(75, 12, &h0000ff) // Output the chart Return c End EventHandler
End Control
End Class
MenuBar MenuBar1
MenuItem FileMenu = "&Ablage"
MenuItem FileQuit = "#App.kFileQuit"
MenuItem EditMenu = "&Bearbeiten"
MenuItem EditUndo = "&Rückgängig"
MenuItem UntitledMenu1 = "-"
MenuItem EditCut = "&Ausschneiden"
MenuItem EditCopy = "&Kopieren"
MenuItem EditPaste = "&Einfügen"
MenuItem EditClear = "#App.kEditClear"
MenuItem UntitledMenu0 = "-"
MenuItem EditSelectAll = "&Alles auswählen"
End MenuBar
Class ChartDirectorControl Inherits Canvas
Const kModeDefault = 0
Const kModeMove = 1
Const kModeZoomIn = 2
Const kModeZoomOut = 3
Event ItemClicked(XLabel As String, X As Integer, DataSet As Integer, DataSetName As String, Value As Double, Title As String, Sector As Integer, Label As String) End Event
Event Open() End Event
Event Render(ViewPort As CDViewPortManagerMBS, Factor As Double, Width As Integer, Height As Integer) As CDBaseChartMBS End Event
EventHandler Function MouseDown(X As Integer, Y As Integer) As Boolean isMouseDown = true If (AllowZoom = True) And (Keyboard.AsyncOptionKey = True) Then // Zoom In Mode = kModeZoomIn StartX = X StartY = Y StartX2 = X StartY2 = Y ElseIf (AllowZoom = True) And (Keyboard.AsyncShiftKey = True) Then // Zoom Out Mode = kModeZoomOut StartX = X StartY = Y StartX2 = X StartY2 = Y Else Dim HotSpotID As Integer // Check if over a hotspot; Nil check prevents spurious NilObjectExceptions If (ImageMapHandler <> Nil) Then HotSpotID = ImageMapHandler.getHotSpot(X, Y) If (HotSpotID > 0) Then // Click Mode = kModeDefault SendClickEvent ElseIf (AllowMove = True) Then // Move Mode = kModeMove MouseCursor = System.Cursors.HandClosed StartX = X StartY = Y ViewPort.startDrag // Call to initiate drag within the ViewPort End If End If Return (True) End EventHandler
EventHandler Sub MouseDrag(X As Integer, Y As Integer) If (Mode = kModeMove) Then // If the chart has been dragged/repositioned then complete the ViewPort drag and refresh the image If (ViewPort.dragTo(CDBaseChartMBS.kDirectionHorizontalVertical, StartX - X, Y - StartY) = True) Then Redraw ElseIf (Mode = kModeZoomIn) Or (Mode = kModeZoomOut) Then // Calculate the coordinates for the scaling/drawing of the selection rectangle Dim L, T, W, H As Integer StartX2 = X StartY2 = Y L = Min(StartX, StartX2) T = Min(StartY, StartY2) W = StartX2 - StartX H = StartY2 - StartY If (W < 0) Then W = -W If (H < 0) Then H = -H // Composite the current chart image with the selection rectangle Graphics.DrawPicture(LastPicture, 0, 0) Graphics.DrawPicture(RectanglePicture, L, T, W, H, 0, 0, RectanglePicture.Width, RectanglePicture.Height) End If End EventHandler
EventHandler Sub MouseExit() // Finalize redraw CleanUp End EventHandler
EventHandler Sub MouseMove(X As Integer, Y As Integer) If (AllowZoom = True) And (Keyboard.AsyncOptionKey = True) Then // Zoom In MouseCursor = System.Cursors.MagnifyLarger CleanUp // Finalize redraw ElseIf (AllowZoom = True) And (Keyboard.AsyncShiftKey = True) Then // Zoom Out MouseCursor = System.Cursors.MagnifySmaller CleanUp // Finalize redraw Else Dim HotSpotID As Integer // Check if over a hotspot; Nil check prevents spurious NilObjectExceptions If (ImageMapHandler <> Nil) Then HotSpotID = ImageMapHandler.getHotSpot(X, Y) If (HotSpotID > 0) Then MouseCursor = System.Cursors.StandardPointer DrawItemInfo(X, Y) ElseIf (AllowMove = True) Then MouseCursor = System.Cursors.HandOpen CleanUp // Finalize redraw Else MouseCursor = System.Cursors.StandardPointer CleanUp // Finalize redraw End If End If End EventHandler
EventHandler Sub MouseUp(X As Integer, Y As Integer) isMouseDown = false If (Mode = kModeMove) Then // Move MouseCursor=System.Cursors.HandOpen ElseIf (Mode = kModeZoomIn) Then // Zoom In (On Point) If (Abs(X - StartX) < 3) And (Abs(Y - StartY) < 3) Then // Zoom In (On Point) If (ViewPort.canZoomIn(CDBaseChartMBS.kDirectionHorizontalVertical) = True) Then If (ViewPort.zoomAt(CDBaseChartMBS.kDirectionHorizontalVertical, X, Y, ZoomInRatio) = True) Then Redraw Else // Cannot zoom in... Redraw Beep End If Else // Zoom In (On Rectangle) If (ViewPort.canZoomIn(CDBaseChartMBS.kDirectionHorizontalVertical) = True) Then If (ViewPort.zoomTo(CDBaseChartMBS.kDirectionHorizontalVertical, X, Y, StartX, StartY) = True) Then Redraw Else // Cannot zoom in... Redraw Beep End If End If ElseIf (Mode = kModeZoomOut) Then // Zoom Out (On Point) If (ViewPort.canZoomOut(CDBaseChartMBS.kDirectionHorizontalVertical) = True) Then If (ViewPort.zoomAt(CDBaseChartMBS.kDirectionHorizontalVertical, X, Y, ZoomOutRatio) = True) Then Redraw else // Cannot zoom out... Redraw Beep End If End If End EventHandler
EventHandler Sub Open() // Initialize the ViewPort manager class ViewPort = New CDViewPortManagerMBS // Set ViewPort coordinant constraints ViewPort.setViewPortHeight(1.0) ViewPort.setViewPortTop(0.0) ViewPort.setViewPortWidth(1.0) ViewPort.setViewPortLeft(0.0) // Set Zoom In and Zoom Out constraints (if desired; otherwise allow infinite zoom) 'ViewPort.setZoomInWidthLimit(0.01) 'ViewPort.setZoomInHeightLimit(0.01) 'ViewPort.setZoomOutWidthLimit(1.0) 'ViewPort.setZoomOutHeightLimit(1.0) // Initialize scalable image for use in rendering the selection rectangle RectanglePicture = New Picture(32, 32, 32) RectanglePicture.Graphics.ForeColor = &cFF0000 RectanglePicture.Graphics.FillRect(0, 0, 32, 32) RectanglePicture.Mask.Graphics.ForeColor = &cCCCCCC RectanglePicture.Mask.Graphics.FillRect(0, 0, 32, 32) // Set the initial cursor style MouseCursor = System.Cursors.HandOpen // Call overridden superclass method Open // fix some properties if set wrong in IDE me.DoubleBuffer = false me.EraseBackground = false #if RBVersion >= 2013.0 then me.Transparent = False #endif End EventHandler
EventHandler Sub Paint(g As Graphics, areas() As REALbasic.Rect) // Handle drawing the composited image to the drawing region Draw(G) End EventHandler
Private Sub CleanUp() // Finalize redraw If (NeedClear = True) Then NeedClear = False if CleanGraphicsBeforeDrawing then Graphics.ClearRect 0,0,Graphics.Width, Graphics.Height end if Graphics.DrawPicture(LastPicture, 0, 0) End If End Sub
Function CurrentPicture() As Picture // Return the currently displayed chart image Return (LastPicture) End Function
Private Sub Draw(G As Graphics) // Prepare ViewPort coordinates for chart rendering ViewPort.validateViewPort // Trigger chart rendering (actual chart configuration and data plotting) Current = Render(ViewPort, 1.0, G.Width, G.Height) If (Current = Nil) Then // If there is no valid chart object returned, clear the drawing region G.ClearRect(0, 0, G.Width, G.Height) Else if RenderWithTransparency then // Get in-memory version of the chart image in PNG format (supports transparency) Dim S As String S = Current.makeChart(CDBaseChartMBS.kPNG) // Error check If (S = "") Then G.ClearRect(0, 0, G.Width, G.Height) Return End If // Generate RB Picture from chart image data LastPicture = PNGStringToPictureMBS(S, 0) else // Get the chart image based on the returned chart object LastPicture = Current.makeChartPicture end if // Support overlay picture drawing If (OverlayPicture <> Nil) Then LastPicture.Graphics.DrawPicture(OverlayPicture, (G.Width - OverlayPicture.Width) / 2, (G.Height - OverlayPicture.Height) / 2) End If // Support control border drawing If (Border = True) Then LastPicture.Graphics.PenHeight = 1 LastPicture.Graphics.PenWidth = 1 LastPicture.Graphics.ForeColor = &c000000 LastPicture.Graphics.DrawRect(0, 0, G.Width, G.Height) LastPicture.Mask.Graphics.PenHeight = 1 LastPicture.Mask.Graphics.PenWidth = 1 LastPicture.Mask.Graphics.ForeColor = &c000000 LastPicture.Mask.Graphics.DrawRect(0, 0, G.Width, G.Height) End If if CleanGraphicsBeforeDrawing then g.ClearRect 0, 0, g.Width, g.Height end if // Draw composited chart image G.DrawPicture(LastPicture, 0, 0) // Update ViewPort to current chart coordinants ViewPort.setChartMetrics(Current.getChartMetrics) // Process ImageMap data to support data point rollovers, etc. Dim ImageMapString As String ImageMapString = Current.getHTMLImageMap("myurl" ) ImageMapHandler = New CDImageMapHandlerMBS(ImageMapString) End If End Sub
Private Sub DrawItemInfo(X As Integer, Y As Integer) Dim Index as integer Dim Key, KeyValue As String Dim XLabel As String Dim XValue As String Dim DataSet As String Dim DataSetName As String Dim Value As String Dim Title As String Dim Sector As String Dim Label As String // Error check If (ImageMapHandler = Nil) Then Return // Do key/value pair priming calls Index = 0 Key = ImageMapHandler.getKey(Index) KeyValue = ImageMapHandler.getValue(Index) Do // NOTE: This is not the complete supported list of parameters; Refer to ChartDirector documentation on 'Parameter Substitution and Formatting' for additional parameters Select Case Key Case "xlabel" XLabel = KeyValue Case "x" XValue = KeyValue Case "dataset" DataSet = KeyValue Case "datasetname" DataSetName = KeyValue Case "value" Value = KeyValue Case "title" Title = KeyValue Case "sector" Sector = KeyValue Case "label" Label = KeyValue End Select Index = Index + 1 // Get next key/value pair Key = ImageMapHandler.getKey(Index) KeyValue = ImageMapHandler.getValue(Index) Loop Until Key.Len = 0 // Compose label graphic If (Label <> "") Then Value = Label + ": " + Value if value = "" then value = Title Dim B As Picture = New Picture(Width, Height, 32) Dim G As Graphics = B.Graphics Dim W As Integer = 6 + G.StringWidth(Value) Dim H As Integer = 20 // Add some space between the cursor position and the initial label drawing position X = X + 10 Y = Y + 10 // If we are too close to the right, we need to move our label drawing position left If (X + W > G.Width) Then X = X - 20 - W // If we are too close to the bottom, we need to move our label drawing position up If (Y + H > G.Height) Then Y = Y - 20 - H // Draw in the current composited chart image B.Graphics.DrawPicture(LastPicture, 0, 0) B.Mask.Graphics.DrawPicture(LastPicture.Mask, 0, 0) // Draw the label graphic G.ForeColor = &cCCCC00 G.FillRect(X, Y, W, H) G.ForeColor = &c000000 G.DrawString(Value, X + 3, Y + 15) // Update the composited chart image Graphics.DrawPicture(B, 0, 0) NeedClear = True End Sub
Sub Print() Dim PS As New PrinterSetup // Set landscape orientation if appropriate PS.Landscape = Width > Height // Initialize graphics region for printing Dim G As Graphics = OpenPrinterDialog(PS) // Bail out... If (G = Nil) Then Return // Render printed chart (use page size) // NOTE: This uses a factor of 4x to up the resolution of the printed chart image (approximately 300 dpi) 'Dim P As Picture = RenderPicture(4, G.Width, G.Height) 'G.DrawPicture(P, 0, 0, G.Width, G.Height, 0, 0, G.Width * 4, G.Height * 4) // Render printed chart (use control size) // NOTE: This uses a factor of 4x to up the resolution of the printed chart image (approximately 300 dpi) Dim P As Picture = RenderPicture(4, Width, Height) G.DrawPicture(P, 0, 0, Width, Height, 0, 0, Width * 4, Height * 4) End Sub
Sub Redraw() // Method to directly trigger internal redraw if RenderWithTransparency then Refresh(true) else Draw(Graphics) end if End Sub
Function RenderPicture(Factor As Double, Width As Integer, Height As Integer) As Picture // Prepare ViewPort coordinates for chart rendering ViewPort.validateViewPort // Handle scaled redraw for printing Dim C As CDBaseChartMBS = Render(ViewPort, Factor, Width, Height) 'If (C <> Nil) Then Return (C.makeChartPicture) // Error check If (C = Nil) Then Return (Nil) // Get in-memory version of the chart image in PNG format (supports transparency) Dim S As String S = C.makeChart(C.kPNG) // Error check If (S = "") Then Return (Nil) // Generate RB Picture from chart image data Dim P As Picture P = PNGStringToPictureMBS(S, 0) Return (P) End Function
Private Sub SendClickEvent() Dim Index as integer Dim Key, KeyValue As String Dim XLabel As String Dim XValue As String Dim DataSet As String Dim DataSetName As String Dim Value As String Dim Title As String Dim Sector As String Dim Label As String // Error check If (ImageMapHandler = Nil) Then Return // Do key/value pair priming calls Index = 0 Key = ImageMapHandler.getKey(Index) KeyValue = ImageMapHandler.getValue(Index) Do // NOTE: This is not the complete supported list of parameters; Refer to ChartDirector documentation on 'Parameter Substitution and Formatting' for additional parameters Select Case Key Case "xlabel" XLabel = KeyValue Case "x" XValue = KeyValue Case "dataset" DataSet = KeyValue Case "datasetname" DataSetName = KeyValue Case "value" Value = KeyValue Case "title" Title = KeyValue Case "sector" Sector = KeyValue Case "label" Label = KeyValue End Select Index = Index + 1 // Get next key/value pair Key = ImageMapHandler.getKey(Index) KeyValue = ImageMapHandler.getValue(Index) Loop Until Key.Len = 0 // Trigger click event ItemClicked(XLabel, Val(XValue), Val(DataSet), DataSetName, Val(Value), Title, Val(Sector), Label) End Sub
Sub UpdateMouseCursor() // call from a timer in your window to keep the mouse cursor change with pressing alt and shift keys. // zoom allowed? if not AllowZoom then Return if isMouseDown then Return dim x as integer = me.MouseX dim y as integer = me.MouseY // check if mouse is inside if X<0 then Return if Y<0 then Return if X>me.Width then Return if Y>me.Height then Return // now update If Keyboard.AsyncOptionKey Then // Zoom In MouseCursor = System.Cursors.MagnifyLarger ElseIf Keyboard.AsyncShiftKey Then // Zoom Out MouseCursor = System.Cursors.MagnifySmaller Else Dim HotSpotID As Integer // Check if over a hotspot; Nil check prevents spurious NilObjectExceptions If (ImageMapHandler <> Nil) Then HotSpotID = ImageMapHandler.getHotSpot(X, Y) If (HotSpotID > 0) Then MouseCursor = System.Cursors.StandardPointer ElseIf (AllowMove = True) Then MouseCursor = System.Cursors.HandOpen Else MouseCursor = System.Cursors.StandardPointer End If End If End Sub
Note "About Properties"
AllowMove and AllowZoom must be enabled if you want to zoom and move. You need to use the viewport in your render code. If Border is true, a black line is drawn around the chart. If CleanGraphicsBeforeDrawing is true then the graphics object is cleared before something is drawn The OverlayPicture defines a picture which is drawn on top of the chart as a watermark. RenderWithTransparency will do redraws slower. The chart is rendered as PNG with mask and for every redraw we refresh the whole control. This may look bad on Windows. ZoomInRatio and ZoomOutRatio are used for zooming. This is how much we zoom on each click.
Note "Notes for Render Event"
Use the Width and Height you get and not the control's. Use the Factor to multiply all values so you get it properly scaled for printing. Use the ViewPort for zooming and moving.
Property AllowMove As Boolean
Property AllowZoom As Boolean
Property Border As Boolean
Property CleanGraphicsBeforeDrawing As Boolean
Property Private Current As CDBaseChartMBS
Property Private ImageMapHandler As CDImageMapHandlerMBS
Property Private LastPicture As Picture
Property Private MaxValue As Double
Property Private MinValue As Double
Property Private Mode As Integer
Property Private NeedClear As Boolean
Property OverlayPicture As Picture
Property Private RectanglePicture As Picture
Property RenderWithTransparency As Boolean
Property Private StartX As Integer
Property Private StartX2 As Integer
Property Private StartY As Integer
Property Private StartY2 As Integer
Property Private ViewPort As CDViewPortManagerMBS
Property ZoomInRatio As Double = 1.25
Property ZoomOutRatio As Double = 0.75
Property Private isMouseDown As Boolean
End Class
End Project

See also:

Feedback, Comments & Corrections

The items on this page are in the following plugins: MBS ChartDirector Plugin.





Links
MBS Xojo Plugins