# Creating QML Controls From Scratch: BarChart

Continuing our QML Controls from Scratch series, this time we will implement a BarChart. Many people who need charts in their application wonder if they need a charting library, but it turns out to be not that difficult to write a custom autoscaled chart.

The public interface consists of a title, yLabel, xLabel, and a list of points, which contains a string for x, a number for y, and a color. Since Rectangle can be used to draw a line, all the pixels are rendered with just Rectangle and Text (as are all the controls so far in this series). The only tricky part is the math necessary to compute the y axis tick lines, which is accomplished via a logarithm.

## BarChart.qml

``````import QtQuick 2.0

Item {
id: root

// public
property string title:  'title'
property string yLabel: 'yLabel'
property string xLabel: 'xLabel'

property variant points: []//{x: 'Zero', y: 60, color: 'red'}, {x: 'One', y: 40, color: 'blue' }]

// private
property double factor: Math.min(width, height)

property double yInterval:  1
property double yMaximum:  10 // set by onPointsChanged
property double yMinimum:   0
function toYPixels(y){return plot.height / (yMaximum - yMinimum) * (y - yMinimum)}

property int    xMaximum:   0 // string length

onPointsChanged: { // auto scale vertically
if(!points)  return
var xMaximum = 0, yMinimum = 0, yMaximum = 0
for(var i = 0; i < points.length; i++) {
if(points[i].y > yMaximum)          yMaximum = points[i].y
if(points[i].y < yMinimum)          yMinimum = points[i].y
if(points[i].x.length > xMaximum)   xMaximum = points[i].x.length
}

var yLog10     = Math.log(yMaximum - yMinimum) / Math.LN10 // take log, convert to integer, and then raise 10 to this power
root.yInterval = Math.pow(10, Math.floor(yLog10)) / (yLog10 % 1 < 0.7? 4: 2) // distance between ticks
root.yMaximum  = Math.ceil( yMaximum / yInterval) * yInterval
root.yMinimum  = Math.floor(yMinimum / yInterval) * yInterval

root.xMaximum  = xMaximum
}

width: 500;  height: 500 // default size

Text { // title
text: title
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: 0.03 * factor
}

Text { // y label
text: yLabel
font.pixelSize: 0.03 * factor
y: 0.5 * (2 * plot.y + plot.height + width)
rotation: -90
transformOrigin: Item.TopLeft
}

Text { // x label
text: xLabel
font.pixelSize: 0.03 * factor
anchors{bottom: parent.bottom;  horizontalCenter: plot.horizontalCenter}
}

Item { // plot
id: plot

anchors{fill: parent;  topMargin: 0.05 * factor;  bottomMargin: (0.015 * xMaximum + 0.05) * factor;
leftMargin: 0.15 * factor;  rightMargin: 0.05 * factor}

Repeater { // y axis tick marks and labels
model: Math.floor((yMaximum - yMinimum) / yInterval) + 1 // number of tick marks

delegate: Rectangle {
property double value: index * yInterval + yMinimum
y: -toYPixels(value) + plot.height
width: plot.width;  height: 1
color: 'black'

Text {
text: parent.value
anchors{right: parent.left;  verticalCenter: parent.verticalCenter;  margins: 0.01 * factor}
font.pixelSize: 0.03 * factor
}
}
}

Repeater { // data
model: points

delegate: Item { // column
width: plot.width / points.length;  height: plot.height
x: width * index

Rectangle { // bar
anchors{horizontalCenter: parent.horizontalCenter
bottom: modelData.y > 0? parent.bottom: undefined;  bottomMargin: toYPixels(0)
top:    modelData.y < 0? parent.top:    undefined;     topMargin: plot.height - toYPixels(0)}
width:  0.7 * parent.width;  height: toYPixels(Math.abs(modelData.y) + yMinimum)
color: modelData.color
}

Text { // x values (rotated -90 degrees)
text: modelData.x
x:   (parent.width - height) / 2
y:    parent.height + width + 0.5 * height
rotation: -90
transformOrigin: Item.TopLeft
font.pixelSize: 0.03 * factor
}
}
}
}

// focus: true
// Keys.onPressed: { // increase values with 0-9 and decrease with Alt+0-9
// if(!isNaN(parseInt(event.text))  &&  parseInt(event.text) < root.points.length) { // 0-9 keys
// var points = root.points
// points[event.text].y = points[event.text].y + (event.modifiers? -0.1: 0.1) * (yMaximum - yMinimum)
// root.points = points
// }
// }
}``````

## Test.qml

``````import QtQuick 2.0

BarChart {
title:  '2015 United States Federal Spending'
yLabel: '\$ Billion'
xLabel: 'Spending Category'
points: [
{x: 'Social Security',  y: 1275.7, color: 'red'    },
{x: 'Medicare',         y: 1051.8, color: 'orange' },
{x: 'Military',         y:  609.3, color: 'gold'   },
{x: 'Interest',         y:  229.2, color: 'cyan'   },
{x: 'Veterans',         y:  160.6, color: 'green'  },
{x: 'Agriculture',      y:  135.7, color: 'blue'   },
{x: 'Education',        y:  102.3, color: 'purple' },
{x: 'Transportation',   y:   85.0, color: 'magenta'},
{x: 'Other',            y:  186.3, color: 'gray'   },
]
}``````

## Summary

In this post, we created a BarChart. Next time we'll create a LineChart. The source code can be downloaded here.