Drawing with device contexts
wxRuby3 provides a rich 2D drawing API built around the concept of a device context (DC). A DC is an abstract drawing surface — the same drawing code works whether you are painting to a panel on screen, composing an image in memory, or writing to an SVG file. You work with a DC by setting pens, brushes, and fonts, then calling drawing methods to place shapes and text.
This lesson covers the fundamentals of DC drawing and builds up to a practical bar chart that resizes correctly with the window.
The paint event
All drawing in wxRuby3 happens inside an evt_paint handler. The event fires whenever the panel needs repainting — on first show, after being uncovered by another window, and after resize. You never call the paint handler directly; the event loop calls it when needed.
|
|
The evt_size handler triggers a repaint when the window is resized. Without it, the drawing would only update on the next natural repaint.
Inside the handler, call paint on the panel to get a DC:
|
|
Never store the DC or use it outside the paint block — it is only valid for the duration of the block.
Step 1 — Pens, brushes, and shapes
A pen defines the colour and width of lines and outlines. A brush defines the fill colour of shapes.
|
|
The pen and brush stay set until you change them — you don’t need to reset them before every draw call, only when you want a different style.
Wx::TRANSPARENT_BRUSH is a pre-defined constant for an invisible fill. Use it whenever you want an outline-only shape.
Step 2 — Text and fonts
Set the font with dc.font = and the text colour with dc.set_text_foreground:
|
|
Wx::Font.new takes four arguments: point size, family, style, and weight. The font families you will use most are Wx::FONTFAMILY_DEFAULT (system UI font) and Wx::FONTFAMILY_TELETYPE (monospace).
Measuring text
Use dc.get_text_extent(text) to measure a string before drawing it. This returns [width, height] in pixels for the current font:
|
|
Measuring text is essential for centring labels above bars in a chart, right-aligning numbers in a table, or fitting text within a fixed-width area.
Draw order
Later draws paint over earlier ones. To draw a bounding box around text without obscuring it:
|
|
Step 3 — Coordinates and client size
All coordinates are in pixels from the top-left corner of the panel. @panel.client_size returns [width, height] — use these to make your drawing adapt to the panel’s current size:
|
|
Because client_size is read fresh inside the paint handler on every repaint, the drawing automatically adapts to any window size. This is why evt_size { @panel.refresh } produces correctly resizing graphics — the refresh triggers a repaint, and the repaint reads the new size.
Step 4 — Bar chart
A bar chart ties everything together — calculating bar positions from data, drawing filled rectangles, centering text labels, and adapting to the panel size.
The key calculation: given a chart area of height chart_h and a maximum data value max_value, the pixel height of a bar with value v is:
|
|
For centring a label above a bar of width bar_width:
|
|
See drawing_demo.rb for the complete chart implementation including gridlines, axis labels, and value labels above each bar.
What to take forward
- All drawing happens inside
@panel.paint { |dc| }— never outside it evt_size { @panel.refresh }makes the drawing resize correctly- Pen = line/outline style; brush = fill style
dc.get_text_extent(text)returns[width, height]for the current font — use it to centre and align text- Read
@panel.client_sizeinside the paint handler to get the current dimensions - Draw order matters — later draws paint over earlier ones; use
Wx::TRANSPARENT_BRUSHfor outline-only shapes
Previous: Module 3 | Next: Images and bitmaps