Four visual themes for the WinCC OA trend widget, two light and two dark, in one panel and one CTRL library.
The widget is a composite. Its container, its legend and its splitter handle are ordinary Qt widgets and take a Qt style sheet; its plot area, grid, scales and curves are drawn by WinCC OA and follow the widget's own attributes. A theme writes both halves.
| Theme | Mode | Intended use |
|---|---|---|
| Paper | light | An editorial figure for a printed report. Warm paper, one hairline of ink, a comparison run in pale grey, the legend as a caption. |
| Blueprint | light | An engineering sheet. Cold graph paper, a hatched area under the flow, a stepped digital signal on a fixed -1..3 scale, monospaced numbers, an HTML title block in the corner of the plot. |
| Console | dark | The control room at night. Near black, colour-blind safe pens, thick strokes, a translucent fill on the lead variable, alarm limits on the chart, one value scale. |
| Slate | dark | Classic dark mode for a plant dashboard. Cool slate greys, one indigo accent, a rounded card with air around the plot, a two-level grid, a soft wash under the lead series, the value axis on the right. |
- WinCC OA 3.21
- Nothing else. No datapoints, no archive, no driver and no peripherals: the demo
series are generated in CTRL and written into the curves with
curveVals, so the panel draws the same thing on a clean installation as it does on a live plant.
-
Copy
panels/trendStyles.xmlinto<project>/panels/. -
Copy
scripts/libs/trendStyles.ctlinto<project>/scripts/libs/. -
Add two entries to the
[ui]section of<project>/config/config:[ui] # Value scale numbers horizontal instead of rotated ninety degrees. trendHorizontalScaleText = 1 # Legend shows a coloured curve sample next to the series name. trendLegendStyle = 0
-
Open the panel:
WCCOAui -p trendStyles.xml, or from GEDI.
One call per trend shape, from the Initialize of the panel that owns it:
#uses "trendStyles.ctl"
main()
{
TS_Paper(getShape("trPaper"));
TS_Blueprint(getShape("trBlueprint"));
TS_Console(getShape("trConsole"));
TS_Slate(getShape("trSlate"));
}
Each theme is five steps: the look, the addCurve calls, the per-curve style,
the data, and the scales.
The demo generator is seeded with a fixed constant, so the same screenshot comes out of every machine and every run: a visual difference means a real change of style, not a different random walk.
The style sheet is built from small named strings, so each rule can carry the reason it exists:
string TS_QssConsole()
{
// A raised card on a darker desk. The rounded corner alone carries it:
// a hairline border at this contrast reads as a scratch on the screen.
string frame = "TrendQT {"
+ " background-color: #16181D;"
+ " border: none;"
+ " border-radius: 6px;"
+ " padding: 10px 12px 6px 6px;"
+ "}";
// A wide transparent handle instead of a grey bar: the gap between chart
// and readout becomes air, which is what separates them in the design.
string handle = "QSplitter::handle {"
+ " background: transparent;"
+ " height: 10px;"
+ "}";
// legend, label and box, the same way: the readout card, the series
// names and the check boxes.
return frame + handle + legend + label + box;
}
Plain attribute writing, in one pass, because every write can trigger a redraw of the whole widget:
setMultiValue(
shp, "styleSheet", TS_QssConsole(),
shp, "manageCommands", FALSE, // hide the built-in command bar
shp, "backCol", "{22,24,29}", // the plot
shp, "legendColor", "{31,36,44}", // and the legend, separately
shp, "foreCol", "{124,133,148}", // time axis and plot frame
shp, "gridColor", "{38,43,52}", // value grid
shp, "gridColorX", "{38,43,52}", // time grid, separately again
shp, "gridLineType", "[dashed,oneColor,JoinMiter,CapButt,1]",
shp, "areaMargins", 0, 10, 6, 24, 2,
shp, "timeFormat", 0, TRUE, "%H:%M", "%d/%m");
| Part of the widget | Qt class | Style sheet |
|---|---|---|
| Container, padding, card background | TrendQT (QSplitter) |
yes |
| Legend background, border, font, padding | TrendLegend |
yes |
| Bar between legend and plot | QSplitter::handle |
yes, and nothing else reaches it |
| Legend check boxes | QCheckBox |
yes |
| Preview band (viewport) | #viewport_leading, #viewport_window, #viewport_starthandle, #viewport_endhandle, #viewport_trailing - by object name, not by class |
yes, and nothing else reaches it |
| Plot area, grid, curves, scales | TrendPlot, TrendScaleQT, TrendTimeScale, TrendValueScale |
selectors attach, but WinCC OA draws over them: use the attributes |
A selection of the behaviours that shaped this example the most. It is not the complete list, only the ones that come up first when you start styling the widget. All of them measured on WinCC OA 3.21.
border-radius works together with padding. The container is a Qt widget
and rounds its own fill; the plot area inside it is drawn by WinCC OA as a plain
rectangle, and with no padding that rectangle reaches the corners. The padding is
what holds it back:
Outside the rounded corner the widget draws nothing, so the panel shows through.
Put a WinCC OA RECTANGLE under the trend in the card colour, with its own
CornerRadius if the card is rounded, and the two read as one object.
Attribute colours are colorDB names or {r,g,b} literals. Hexadecimal
belongs to the style sheet: setValue(shp, "backCol", "#16181D") returns 0 and
logs Reading color: Color '#16181D' does not exist. The two worlds meet at
exactly that line.
Background and grid are two attributes each. backCol paints the plot and
legendColor the legend; gridColor is the value grid and gridColorX the time
grid. foreCol governs the time axis and the frame of the drawing area.
curveLineType is [shape,colorMode,join,cap,width], with solid and
dashed as the line shapes. Width is in pixels, and 0 draws nothing.
curveFillType takes a WinCC OA fill string: [solid],
[hatch,[parallel,6,left]], [hatch,[cross,10,left]]. A filled curve needs
curveFilled, curveFillType and curveFillColor set together.
curveMinMax applies once autoscale is off. Write curveAutoscale to
FALSE first.
A hand-fed curve needs its unit written. The widget reads _common over an
open connection, so a static curve takes curveLegendUnit explicitly. Leave the
unit out of the series name: the widget appends it itself, and you would get
Feed [m3/h] [m3/h].
Number formats take a field width. curveLegendFormat and curveScaleFormat
want %1.1f rather than %.1f; without a width they fall back to %g and six
significant digits, so a legend with room for 63.8 shows 63.7819.
visibleTimeRange is enough to frame a static curve. trendStop marks the
plot as recording-stopped, and a curve with no data source has nothing to update
anyway.
Styling QCheckBox::indicator replaces the native tick. Qt hands the whole
sub-control over, so ticked and unticked look the same unless the state is
carried by the fill: hollow off, solid on.
A style sheet on the legend makes the series names one colour. WinCC OA can
tint each name with the colour of its curve, and stops once a style sheet touches
the legend. [ui] trendLegendStyle = 0 puts a coloured sample of the curve, with
its real line type, next to the name instead.
Digital signals are stepped and sit on a fixed scale. curveType = 1 holds
the value between two samples, because a pump was not half running in between. On
a fixed -1..3 the trace stays in the lower quarter, reads as a status strip and
leaves the top of the plot to the analogue values.
One value scale, not one per pen. Every visible curve draws its own axis, and three axes on a wide card stack numbers that belong to different units. Showing only the lead curve's scale leaves the plot to the data; the other values are read from the legend, where they carry their own units.
curveScalePosition shares one numbering across both orientations. On a
horizontal trend 2 is left and 3 is right; 0 and 1 are bottom and top, and
belong to a vertical trend. The Slate theme uses 3 on purpose: a right hand axis
is what a web dashboard does.
Group the writes that can be grouped. curveVals, curveAutoscale and
curveMinMax share one setMultiValue, applied in the order they are written, so
autoscale goes off in time for the range. curveScaleVisibility goes in a later
call, once the curve has values.
areaMargins is spacing, not reserved space. The widget makes its own room on
the real width of each visible scale. The right margin is what keeps the last
label of the time axis clear of the edge of the card.
panels/trendStyles.xml the page: four trends, cards and headings
scripts/libs/trendStyles.ctl the four themes and the demo generator
docs/ screenshots
MIT - see LICENSE.
Built by Interial, WinCC OA specialists in Brescia, Italy. WinCC OA and Siemens are trademarks of Siemens AG. This project is not affiliated with, endorsed by, or sponsored by Siemens AG.

