chartcartesian — u_pbt_chartcartesian #
← Component reference · Guide contents
Cartesian chart: columns, bars, lines, areas and scatter, on one category axis and one or two value axes. The type is set per series.
▶ See it live — Demo application, Cartesian chart tile: the preview, the code behind it and this page, side by side.
In brief #
| Userobject | u_pbt_chartcartesian |
| Item class | n_pbt_chartcartesian_series (of_item(key)) · n_pbt_chartcartesian_axis (of_axis(axis)) |
| Used for | Comparing values along an axis — months, products, regions |
| Principle | You set the categories then the series; the scales, the grid and the animation are ours |
Quick start #
// Local variables
string ls_months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun"}
double ld_sales[] = {420, 510, 480, 620, 700, 660}
// The categories FIRST : they fix how many values a series carries
uo_chart.of_set_categories(/*labels*/ ls_months)
uo_chart.of_add_series(/*key*/ "sales", /*label*/ "Revenue", /*type*/ u_pbt_chartcartesian.TYPE_BAR, /*values*/ ld_sales)
The type belongs to the SERIES, not to the component #
This is the decision everything else follows from. One component per type — one for bars, one for lines — can never produce a combination: bars with a target line over them, on a second axis. Yet that is the most asked-for shape on a dashboard.
Here there is one component, and each series carries its is_type (TYPE_BAR, TYPE_LINE, TYPE_AREA, TYPE_SCATTER). Changing one series' type leaves the others alone.
ib_secondary puts a series on the second value axis. That is what lets an order count and a revenue figure live together: without it, one of the two is a flat line along the axis.
of_set_categoriescomes before the series: their number fixes how many values each series must carry.
// Local variables
double ld_target[] = {400, 500, 550, 600, 680, 720}
// Bars and a line in the SAME chart : the type belongs to the series
uo_chart.of_add_series(/*key*/ "target", /*label*/ "Target", /*type*/ u_pbt_chartcartesian.TYPE_LINE, /*values*/ ld_target)
// And its own scale, when the two do not share a unit
uo_chart.of_item(/*key*/ "target").ib_secondary = true
Properties #
| Property | Type | Default | Role |
|---|---|---|---|
is_title | string | "" | Chart title, shown above |
ib_show_legend | boolean | true | Shows the legend. Clicking an entry hides its series — the cheapest way to compare two series out of five |
is_legend_location | string | bottom | Where the legend goes (LEGEND_* constants): above or below the plot, or beside it — START/END put it in a column on one side, and the plot gives up that room. START/END are logical: they follow the reading direction. An unknown place is the bottom, and reads back as such |
ib_show_grid | boolean | true | The rules behind the plot, at every round value. Without them only the shape stays readable, never a value |
ib_value_tooltip | boolean | true | The value panel: when the mouse passes over a category, every series and its value there, at once, and a crosshair marks the column. It is not is_tooltip (inherited), which is the tooltip of the whole component — this one follows the pointer and says what the chart is made of. Each series writes its line with its own is_label_format; a missing value has no line |
is_orientation | string | vertical | Columns or bars (ORIENTATION_* constants). Turns the whole frame of reference, lines and areas included |
ib_stacked | boolean | false | Stacks the bar series instead of setting them side by side. Lines and areas are never stacked: they say a level |
is_stack_mode | string | value | Sum or share (STACK_* constants). In percentage every stack is brought to 100: the axis then reads shares, not totals — down to -100 when some shares are negative. Means nothing outside stacking |
ib_enabled | boolean | true | Greyed out: the component keeps showing what it shows — a chart with no data is a different thing from a chart the application has switched off — but it stops answering: no click, no key, no legend entry, and it leaves the Tab order. true by default |
ib_animated | boolean | true | The chart arrives animated: the shapes grow, the ribbons fade in. If the arrival replays on every refresh of a dashboard, better turn it off — an arrival that restarts every five seconds never finishes. A workstation set to reduce motion never animates, whatever this says |
ii_anim_duration | integer | 450 | Duration of that arrival, in milliseconds (0 to 5000) |
is_anim_easing | string | ease-out | Curve of the arrival (EASING_* constants): linear, ease-out, back-out which overshoots and comes back, bounce-out which bounces, elastic-out which snaps. This is what gives an animation its character |
ii_anim_delay | integer | 0 | Delay between two series on arrival, in milliseconds (0 to 1000). They then come in one after another instead of appearing together, which gives the chart its reading order. 0 = all at once |
is_theme_style | string | "" | Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change |
is_theme_mode | string | "" | Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change |
il_theme_accent | long | -1 | Accent colour of this component (-1 = the theme's accent) |
is_tooltip | string | "" | Plain tooltip shown when hovering the component |
is_super_tooltip_title | string | "" | Title of the rich tooltip (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the rich tooltip (rich markup accepted) |
is_super_tooltip_image | string | "" | Image of the rich tooltip |
Methods #
| Method | Role | |
|---|---|---|
of_set_categories ( string as_labels[] ) | The labels of the category axis. Call it before the series. Returns 0 once applied, -2 when the component is not created | |
of_set_value_labels ( string as_labels[] ) | Labels on the value axis. Empty — the normal case — that axis carries numbers. Filled, it carries bands like the category axis: the two-categorical frame a heat map is drawn in. Returns 0 once applied, -2 when the component is not created | |
of_get_value_labels ( ref string as_labels[] ) | Fills as_labels[] with what the value axis carries right now and returns the count. Zero = it carries numbers | |
of_add_heat_series ( string as_key, string as_label, long al_x[], long al_y[], double ad_weight[] ) | A heat map: one cell per (category, value label) pair, coloured by its weight. al_x is the rank of the category and al_y the rank of the label, both from 1 — the ranks a PowerScript array is indexed by, which a double loop writes without arithmetic. Set both label lists first. Returns 0 once added, -5 when the key is empty, holds / or ` | , or is already held, -2` when the component is not created |
of_add_ohlc_series ( string as_key, string as_label, double ad_open[], double ad_high[], double ad_low[], double ad_close[] ) | An OHLC series: the range from low to high, a tick on the left for the open and one on the right for the close. Four numbers per point, and the axis is read against the high and the low — the other two are inside them by construction. The four arrays are truncated to the shortest. Returns 0 once added, -5 when the key is empty, holds / or ` | , or is already held, -2` when the component is not created |
of_add_candle_series ( string as_key, string as_label, double ad_open[], double ad_high[], double ad_low[], double ad_close[] ) | The same four numbers, drawn as candles: a wick from low to high and a filled body between the open and the close. A doji — open equal to close — keeps one pixel of body, so it stays a line the eye can find. Returns 0 once added, -5 when the key is empty, holds / or ` | , or is already held, -2` when the component is not created |
of_set_series_ohlc ( string as_key, double ad_open[], double ad_high[], double ad_low[], double ad_close[] ) | New quotes for a financial series already on the chart. What of_set_series_values is to a series of values. Returns 0 once applied, -5 when no series has this key or when it is not a financial series, -2 when the component is not created | |
of_get_series_ohlc ( string as_key, ref double ad_open[], ref double ad_high[], ref double ad_low[], ref double ad_close[] ) | Fills the four arrays with what the series carries right now and returns the number of points. A series that is not financial answers 0 | |
of_add_series ( string as_key, string as_label, string as_type, double ad_values[] { , long al_color } ) | Adds a series with its type — TYPE_BAR, TYPE_LINE, TYPE_AREA, TYPE_SCATTER or TYPE_STEPLINE; the overload also takes its colour (RGB()), otherwise the palette picks. A NULL value (SetNull) is a gap: nothing is drawn, labelled or clickable there, and a line is cut. A key already held is refused, never replaced: of_set_series_values and the of_item handle change a series that exists. Returns 0 once added, -5 when the type is not one of those five (a heat map and the financial series have their own method), or when the key is empty, holds / or ` | , or is already held, -2` when the component is not created |
of_set_series_values ( string as_key, double ad_values[] ) | New values for a series already drawn, everything else untouched. This is what a live dashboard calls: the series keeps its identity, so the shapes slide instead of jumping. A NULL value is a gap. Returns 0 once applied, -5 when no series has this key or when it carries points or quotes (of_set_series_points, of_set_series_ohlc replace those), -2 when the component is not created | |
of_append_values ( string as_key, double ad_values[], long al_max_points ) | A live chart: pushes new values at the end of a series and drops the oldest once it carries more than al_max_points (0 = no limit). Only the new values travel — the series is not sent again on every tick. Pair it with of_append_categories. Returns 0 once applied, -5 when no series has this key or when it carries points or quotes, -2 when the component is not created | |
of_append_categories ( string as_labels[], long al_max_count ) | New categories at the end of the axis, the oldest dropped beyond al_max_count (0 = no limit): the labels of a live chart slide with its values. Returns 0 once applied, -2 when the component is not created | |
of_add_point_series ( string as_key, string as_label, double ad_x[], double ad_y[] { , double ad_weight[] } ) | A series positioned by value on both axes: x says where along the category axis, y how high. That axis then stops being a category axis — it becomes a second value axis, with its own graduations. An overload takes a weight per point, which sizes the bubble. Returns 0 once added, -5 when the key is empty, holds / or ` | , or is already held, -2` when the component is not created |
of_set_series_points ( string as_key, double ad_x[], double ad_y[], double ad_weight[] ) | New points for a series that already carries some. What of_set_series_values is to a series of values. Returns 0 once applied, -5 when no series has this key or when it carries values or quotes, -2 when the component is not created | |
of_get_series_points ( string as_key, ref double ad_x[], ref double ad_y[], ref double ad_weight[] ) | Fills the three arrays with what the series carries right now and returns the number of points. A series of values answers nothing in ad_x: it has no x of its own | |
of_get_series_values ( string as_key, ref double ad_values[] ) | Fills ad_values[] with what the series carries right now and returns the count. A live read : it asks the chart, not the last push | |
of_remove_series ( string as_key ) | Removes one series; the others keep their colour and visibility. Its handle (of_item) is destroyed with it: a later of_item on the same key creates a fresh one. Returns 0 once removed, -5 when no series has this key, -2 when the component is not created | |
of_clear ( ) | Empties the chart — series and categories; the series handles go with them. The axes keep their settings and their sections. Returns 0 once cleared, -2 when the component is not created | |
of_set_value_format ( integer ai_decimals, string as_prefix, string as_suffix, boolean ab_thousands ) | Number format of the whole chart — axes, data labels and value panel: decimals, prefix, suffix, thousands separator. The decimal and thousands separators are those of the display language. It also clears the format of each axis. Returns 0 once applied, -2 when the component is not created | |
of_set_value_format ( string as_axis, integer ai_decimals, string as_prefix, string as_suffix, boolean ab_thousands ) | The format of one axis (AXIS_VALUE, AXIS_VALUE2, or AXIS_CATEGORY when it carries numbers): its graduations, and the labels and the panel of the series drawn on it — an amount on one side, a count of orders on the other. Returns 0 once applied, -5 when the axis is not one of AXIS_*, -2 when the component is not created | |
of_clear_value_format ( ) | Back to the default format. Returns 0 once applied, -2 when the component is not created | |
of_item ( string as_key ) | Returns a series handle, to change it later | |
of_axis ( string as_axis ) | Returns an axis handle (AXIS_CATEGORY, AXIS_VALUE, AXIS_VALUE2), to set it up afterwards. Any other role gives no handle (IsValid() is false) | |
of_add_axis_section ( string as_axis, string as_key, double ad_from, double ad_to, long al_color ) | A band across the plot, behind the data: "below 200 is bad". Drawn under the shapes, it never competes with them. On the category axis, ad_from and ad_to are positions counted from 1 (2 to 3 covers the second and the third category). Returns 0 once added, -5 when the axis is not one of AXIS_* or when the key is empty, holds / or ` | , or is already held on that axis, -2` when the component is not created |
of_add_axis_threshold ( string as_axis, string as_key, double ad_at, long al_color, integer ai_thickness ) | A line across the plot at one value — a target, a limit. Same registry as the bands, but drawn to be seen. Returns 0 once added, -5 when the axis is not one of AXIS_* or when the key is empty, holds / or ` | , or is already held on that axis, -2` when the component is not created |
of_clear_axis_sections ( string as_axis ) | Removes every band and every threshold of one axis. Returns 0 once cleared, -5 when the axis is not one of AXIS_*, -2 when the component is not created | |
of_reset ( ) | Empties the chart — series and axis handles included — and puts every property back to its default. Returns 0 once done, -2 when the component is not created |
Without a key (demo mode), the chart draws only its first 3 series, legend included. The others stay in the model —
of_count,of_get_series_valuesand their handles see them — and setting the key shows them without you sending anything again.
Events #
| Event | Fired when |
|---|---|
ue_point_clicked (string as_key, long al_index, double ad_value) | A bar, a point, a cell or a quote was clicked — or confirmed with Enter: the series key, the position of the point in the series, counted from 1, and its value — the point's own value whatever the shape of the series: the y of a scatter, the weight of a heat cell, the close of a quote. On the keyboard Left/Right walk the categories and Up/Down choose the series the panel underlines; a point series or a heat map, once chosen, is walked point by point. A gap has nothing to click |
ue_series_toggled (string as_key, boolean ab_visible) | The user hid or restored a series with a click in the legend. What the chart displays is then no longer what the application sent. Setting ib_visible is a value, like ticking a check box: this event reports the user's gesture |
The animation is a way in, not a computation: the final geometry is written straight away, and only the arrival is played. A chart that is never displayed — born off screen, on a tab nobody opened — therefore shows the right bars, silently.
Item properties #
| Property | Type | Default | Role |
|---|---|---|---|
is_label | string | "" | The name of the series, as the legend shows it |
is_type | string | bar | bar · line · area · scatter · stepline (TYPE_* constants). Changes the drawing of this series alone — that is what makes a combination chart. Only a type of the same family is taken: a series of values among bar, line, area, scatter and stepline, a financial series between ohlc and candle; any other is ignored and is_type reads back the type kept |
il_color | long | -1 | Colour of the series (RGB()). -1 while none is set: the palette picks |
ib_visible | boolean | true | false hides a series without removing it — the legend keeps the way back. Like a check box, it is a value you set: ue_series_toggled reports the user's click in the legend |
ib_secondary | boolean | false | Plots the series on the second axis. That is how a count and an amount live on the same chart |
ib_data_labels | boolean | false | Writes each point's value next to it. Off by default: on fifty points the labels cover the drawing they explain |
is_label_format | string | "" | What each label and each line of the panel says: #y# the value, #x# the category (the x of a point series, the column of a heat cell), #v# the row of a heat cell, #label# the name of the series, #open# #high# #low# #close# the quotes; the rest is written as it stands — "$#y#K". Empty = the value alone. A label written inside a stacked bar takes black or white, whichever its bar carries |
id_smoothness | double | 1 | Lines and areas only: how round the line is drawn, 0 = straight segments, 1 = fully curved, as LiveCharts draws it. A step line ignores it — it says the level HELD until the next category, and a curve would say it drifted. A curve can pass beyond its points; it is cut to the plot frame and never paints over the axis labels |
ib_fill | boolean | true | Lines and areas: the wash under the stroke, down to the axis, in the series' own colour. It is the removal that is the deliberate act — for a line laid over something else (columns, coloured axis bands) that the wash would hide |
il_fill_color | long | -1 | The colour of that wash. -1 = the colour of the line itself |
is_marker | string | "circle" | Lines, step lines and scatters: the SHAPE of the marker on each point (constants MARKER_*: none, circle, square, triangle, diamond). Not decoration — two lines that cross, printed in grey, are told apart by their markers and by nothing else |
ii_thickness | integer | 2 | Lines and areas: the thickness of the stroke, in pixels |
ii_point_count | integer | 0 | How many points this series carries — values or (x, y) pairs (read only) |
il_cold_color | long | -1 | Heat maps only: the COLD end of the ramp, the lightest reading. -1 = the component's own ramp, one blue, from very pale to deep |
il_mid_color | long | -1 | The MIDDLE stop. Three stops and not two: interpolating from one end to the other washes out the middle of the scale, which is where most readings sit. -1 = the component's own middle blue |
il_hot_color | long | -1 | The HOT end, the heaviest reading. It is the one the legend shows. -1 = the component's own deep blue |
il_up_color | long | -1 | Financial series only: the colour of a point that closes above its open. -1 = the component's own green |
il_down_color | long | -1 | And that of a point that closes below. Two colours because the direction is what such a chart is read for — one colour would hide it. -1 = the component's own red |
Axis properties #
| Property | Type | Default | Role |
|---|---|---|---|
is_title | string | "" | Title written along the axis. Empty = no title, and no room reserved for one |
id_min | double | NULL | Imposed lower bound. It wins over the data and is not rounded: someone who writes 0 means 0. NULL (SetNull) imposes nothing: the axis follows its data again, and a free bound reads back NULL (IsNull), never 0. Free, a value axis takes zero in only when a bar or an area stands on it: quotes and lines are read on their own range |
id_max | double | NULL | Imposed upper bound, same rule. What goes past it is cut at the edge of the plot |
id_step | double | 0 | Distance between two grid lines, in values. 0 = a round step read from the range |
ii_label_rotation | integer | 0 | Rotation of this axis' labels, in degrees (-90 to 90) — category names or value graduations. Twelve month names only fit under a chart when they lean; when they still do not, the chart writes one in N, the first and the last always |
ib_separator | boolean | true | Its grid lines. A category axis rarely needs them, a value axis nearly always does |
ii_section_count | integer | 0 | How many bands and thresholds this axis carries (read only) |
ib_logarithmic | boolean | false | Value axes only: a logarithmic scale, graduated by decades (1, 10, 100…), each decade the same height — for figures that span several orders of magnitude. Zero and negative values have no place on it and are not drawn |
Examples #
Setting the animation to suit the screen #
// A dashboard that refreshes every few seconds : no arrival animation
uo_chart.ib_animated = false
// Elsewhere, a slower arrival that is actually seen
uo_chart.ii_anim_duration = 1200
Opening the rows behind a point #
// Event ue_point_clicked of uo_chart : the series key, the position of the
// point (from 1) and its value - enough to open the rows behind it
string ls_month
// On a series of values, the position IS the rank of the category
ls_month = is_months[al_index]
wf_open_orders(/*series*/ as_key, /*month*/ ls_month, /*amount*/ ad_value)
A live dashboard #
// Timer event of the window : new values for the same series, so the bars
// SLIDE to their new height instead of replaying their arrival
double ld_sales[]
// Read the figures, then hand them to the series already on the chart
ld_sales = wf_read_sales()
uo_chart.of_set_series_values(/*key*/ "sales", /*values*/ ld_sales)
A live chart that slides #
// Timer event of the window, every second : ONE new reading. Only that value
// travels ; past sixty, the oldest leaves on both the axis and the series
// Local variables
string ls_time[]
double ld_cpu[]
// The time of the reading on the axis, the reading itself in the series
ls_time[1] = String(Now(), "hh:mm:ss")
ld_cpu[1] = wf_read_cpu()
uo_chart.of_append_categories(/*labels*/ ls_time, /*max_count*/ 60)
uo_chart.of_append_values(/*key*/ "cpu", /*values*/ ld_cpu, /*max_points*/ 60)
A missing reading, and the chart as a picture #
// March was not measured : a NULL value is a gap, the line is cut there
// Local variables
double ld_temp[] = {18.5, 19.2, 0, 21.4}
// The third reading is missing : NULL, not zero
SetNull(ld_temp[3])
uo_chart.of_add_series(/*key*/ "temp", /*label*/ "Temperature", /*type*/ u_pbt_chartcartesian.TYPE_LINE, /*values*/ ld_temp)
// The chart as it is on screen, title and legend included, to a PNG file
uo_chart.of_save_as_png(/*path*/ "C:\Reports\temperature.png")
A band and a target on the value axis #
// A pale red band below 200 and a target line at 600, behind the bars
uo_chart.of_add_axis_section(/*axis*/ u_pbt_chartcartesian.AXIS_VALUE, /*key*/ "low", /*from*/ 0, /*to*/ 200, /*color*/ RGB(/*red*/ 255, /*green*/ 220, /*blue*/ 220))
uo_chart.of_add_axis_threshold(/*axis*/ u_pbt_chartcartesian.AXIS_VALUE, /*key*/ "target", /*at*/ 600, /*color*/ RGB(/*red*/ 200, /*green*/ 70, /*blue*/ 70), /*thickness*/ 2)
// The value axis says what it measures
uo_chart.of_axis(/*axis*/ u_pbt_chartcartesian.AXIS_VALUE).is_title = "Sales ($K)"
Good practice #
- Set the categories before the series. Their number says what a series must contain; the other way round cannot work.
- A second axis is justified, not endured. Two scales on one drawing mislead the eye: bring it out only when the units really differ.
- Turn the animation off on a refreshing dashboard. An arrival that restarts every five seconds never finishes, and tires instead of helping.
- Bars start at zero, and the component keeps to that: zero joins the axis as soon as a bar or an area stands on it. A truncated bar chart exaggerates every gap. Lines, scatters and quotes are read on their own range.
- The chart keeps its mathematical direction in a right-to-left application: categories from left to right, value axis on the left. Only the
START/ENDlegend follows the reading direction. - Stack to read a total, juxtapose to compare. These are not two styles, they are two different questions.
ue_point_clickedgives the rank: use it to open the exact list behind that point, not the search screen.
Inherited from the common base #
These members exist on every visual component — they are not specific to this one. They are detailed once, in the transverse chapters; this table only says where to read them.
| Members | Role | Detailed in |
|---|---|---|
of_count · of_keys_at · of_has | Walk what the component holds | 3.2 Items |
of_reset | Put the component back to zero | 3.6 Resetting a component: of_reset() |
of_register_shortcut · of_clear_shortcuts | The component's keyboard chords | 3.5 Keyboard shortcuts |
of_is_created · of_is_ready · of_get_last_error | Whether it was born, whether it is ready, what failed | 3.7 Diagnostics |
of_save_as_png · of_save_as_jpg | Export the rendering as an image | 3.8 Exporting the rendering as an image |
of_set_redraw | Group changes into a single repaint | 3.10 Best practices |
of_preload_icons | Icons shown with no delay | Instant display: of_icon |
of_set_translation | Translate one of the component's labels | 5.2 Adapting a label: of_set_translation |
of_focus_webview | Give the component the focus | 6.4 Keyboard and focus |
of_print · of_print_to_pdf | Print, or write a PDF | 6.9 Printing |
of_set_property · of_get_property · of_component_name | Driving a property by its name | 3.1 The property engine |
Two helpers are not inherited: of_icon and of_escape_markup live on n_pbt_utils. Declare one — n_pbt_utils lnv_utils, nothing to create — and call them on it.