Last week we looked at how to use the Line Chart Widget. This week I am going to show you how to modify this widget. I have included a link to the code on GitHub at the end of this blog post.
Having spent most of my time lately immersed in mergExt externals, the LiveCode engine and the IDE, I haven’t had as much time as I’d like to delve into LiveCode Builder and Widgets. Until now…
The showLines
I had identified a few weeks ago that the line chart widget is so very close to a scatter plot that it hurts, so my first port of call was to add a showLines property. It turns out it was relatively easy to do. I first implemented a property definition along with documentation. Here I needed to force the Property Inspector to use the Basic action because otherwise the inspector would put it on the table section, because the field showLines property appears there:
/**
Summary: Whether chart lines are displayed
Syntax:
set the showLines of to { true | false }
get the showLines of
Value (boolean): True if lines should be shown; false otherwise
Description:
If true, the graph widget will draw graph lines on the graph.
Related: markerStyles (property)
*/
property showLines get mLinesShow set SetLineVisibility
metadata showLines.default is "true"
metadata showLines.label is "Show graph lines"
metadata showLines.section is "Basic"
I added an instance variable:
private variable mLinesShow as Boolean
Initialise in the OnCreate handler:
put true into mLinesShow
I added showLines to the OnSave handler:
put mLinesShow into rProperties["showLines"]
Then to the OnLoad handler:
if "showLines" is among the keys of pProperties then
SetLineVisibility(pProperties["showLines"])
end if
I implemented the property setter:
public handler SetLineVisibility(in pShowLines as Boolean) returns nothing
put pShowLines into mLinesShow
redraw all
end handler
Then in the drawGraph handler I just ensured mLinesShow was true before drawing the lines:
if mLinesShow is true then
if tPointIndex is 1 then
move to tPoint on tPath
else
line to tPoint on tPath
end if
end if
...
if mLinesShow is true then
stroke tPath on this canvas
end if
The markerStyles
Now that showLines was implemented the next logical step was to add a way to set the marker styles for each point on the chart. I wanted to be able to both have a chart with lines but no markers and different markers for each line. I realised that the showLines and the markerStyles needed to work together to ensure that there were always either lines or markers and possibly both. There was already an instance variable, mVerticesShow, that governed the visibility of the markers but was always true. I decided not to expose that to script, but to use it to govern whether no makers should be shown at all. In this way we can set the markerStyles to empty and therefore not show any markers, or set one style for each data series.
Again the first thing I did was implement the property definition and documentation. Here I needed to set the editor type so the inspector used a large field with a scrollbar:
/**
Summary: Set the marker style for each data series
Syntax:
set the markerStyles of to { | empty }
get the markerStyles of
Value(String): A line-delimited list of the marker styles for each line
on the graph
Description:
The are the marker styles of each line in the graph
widget.
If set to empty then no markers will be shown and
showLines will be set to true. Available marker styles may be one of:
- "filled circle"
- "filled square"
- "filled diamond"
- "circle"
- "square"
- "diamond"
By default the markerStyles will repeat in the above order.
Related: showLines (property)
*/
property markerStyles get GetMarkerStyles set SetMarkerStyles
metadata markerStyles.default is "filled circlenfilled squarenfilled diamond"
metadata markerStyles.editor is "com.livecode.pi.text"
metadata markerStyles.label is "Marker styles"
Add an instance variable:
private variable mMarkerStyles as List
Initialise in the OnCreate handler:
put [] into mMarkerStyles
I added markerStyles and showMarkers to the OnSave handler:
put mVerticesShow into rProperties["showMarkers"]
put mMarkerStyles into rProperties["markerStyles"]
Then in the OnLoad handler we set the markers:
if "showMarkers" is among the keys of pProperties then
put pProperties["showMarkers"] into mVerticesShow
end if
if "markerStyles" is among the keys of pProperties then
put pProperties["markerStyles"] into mMarkerStyles
end if
EnsureMarkerStyles()
I implemented the property getter, setter and a function to ensure the markers are initialised correctly:
public handler GetMarkerStyles() returns String
if mMarkerStyles is [] then
return ""
end if
variable tMarkerStyles as String
combine mMarkerStyles with newline into tMarkerStyles
return tMarkerStyles
end handler
public handler SetMarkerStyles(in pMarkerStyles as String) returns nothing
if pMarkerStyles is "" then
put true into mLinesShow
put [] into mMarkerStyles
put false into mVerticesShow
else
split pMarkerStyles by newline into mMarkerStyles
put true into mVerticesShow
EnsureMarkerStyles()
end if
redraw all
end handler
public handler EnsureMarkerStyles() returns nothing
if mVerticesShow and the number of elements in mMarkerStyles < the number of elements in mData then
variable tStyles as List
put ["filled circle", "filled square", "filled diamond", "circle", "square", "diamond"] into tStyles
variable tCount as Integer
repeat with tCount from the number of elements in mMarkerStyles + 1 up to the number of elements in mData
push tStyles[tCount mod 6 + 1] onto back of mMarkerStyles
end repeat
end if
end handler
I added EnsureMarkerStyles() to the setData property to make sure that there will always be enough marker styles for the number of series in the widget.
Then in the drawGraph handler I created the different paths based on the required marker style for each line:
// Draw vertices
if mVerticesShow is true then
if mMarkerStyles[tGraphLine - 1] contains "circle" then
put circle path centered at point [0,0] with radius 3 into tMarkerPath
else
put rectangle path of rectangle [-3,-3,3,3] into tMarkerPath
end if
if mMarkerStyles[tGraphLine - 1] contains "diamond" then
rotate tMarkerPath by 45
end if
translate tMarkerPath by [tPointX, tPointY]
add tMarkerPath to tPathVertices
end if
...
if mVerticesShow is true then
if mMarkerStyles[tGraphLine - 1] contains "filled" then
fill tPathVertices on this canvas
else
stroke tPath on this canvas
end if
end if
The End Result
You can view the code on GitHub here
2 comments
Join the conversationPaul Malloy - September 15, 2016
Pardon a rookie question—
Where can I find the documentation for the various LineChart commands, such as set markerStyle? I cannot see any when I search…
panos - September 16, 2016
Hi Paul,
Just open the Dictionary, and from the “Choose API:” dropdown menu choose “Line Graph”. This contains all the Line graph related entries