I am using C++11 with GNU tool chain with gtkmm3, on Ubuntu 12.04 LTS 32 bit. I have been playing wtih some of the examples for gtkmm3 in Programming with gtkmm 3.
Based on 17.2.1.Example there, I inherited from Gtk::DrawingArea
(MyDrawingArea
here) and overrode the on_draw()
event handler as follows:
MyDrawingArea.hpp
...
protected:
bool on_draw ( const Cairo::RefPtr<Cairo::Context>& cr ) override;
MyDrawingArea.cpp
bool MyDrawingArea::on_draw( const Cairo::RefPtr<Cairo::Context>& cr )
{
Gtk::Allocation allocation = get_allocation( );
const int width = allocation.get_width( );
const int height = allocation.get_height( );
int coord1{ height - 3 };
cr->set_line_width( 3.0 );
this->get_window( )->freeze_updates( );
cr->set_source_rgb( 0, 0.40, 0.60 );
cr->move_to( 0, coord1 );
cr->line_to( width, coord1 );
cr->stroke( );
cr->set_source_rgb( 1, 0.05, 1 );
cr->move_to( mXStart, coord1 );
cr->line_to( mXStart, mYAxis * 1.5 );
cr->show_text( to_string( mYAxis ) );
cr->stroke( );
mXStart += 5;
this->get_window( )->thaw_updates( );
return true;
}
My goal is to draw a simple bar graph based on a calculation I do in a little test application, the idea being that each time the on_draw()
event is called, the next bar would be moved 5 units to the right on mXAxis
and a vertical line would be drawn based on the new mYaxis
value, which is computed based on the results of the new calculation.
When I want to repaint my graph and trigger the MyDrawingArea::on_draw() event, I call MyDrawingArea.show_all() from my application after the calculation has completed, and new x and y axes have been set.
However, this does not work as I expected: MyDrawingArea.show_all()
invalidates the entire drawing window and draws from scratch: the new graph line appears in its proper place, but the previous ones are erased. I also tried MyDrawingArea.queue_draw()
, which had the same effect. But I want to persist the previous graph results so I can get a profile of the calculation results, as I calculate with different values.
This implementation is also causing the bottom line on my graph (my x axis on the graph)- drawn by the first stroke()
call in my code example, to be rendered anew on each call to on_draw() - although this should not be necassary since this line persists for the lifetime of MyDrawingArea
- it should not be necessary to invalidate and then re-draw it on each new on_draw()
event, as my code is currently doing, because I am haven't yet found a way to handle this.
I am very new to Cairo, so I'm sure I'm probably doing this completely wrong, but explicit, task-oriented documentation appears to be sparse - have not found anything that explains how to do this, although I'm sure it is quite simple.
What do I need to do to draw a new line on Gtk::DrawingArea
, while persisting previous graph lines that have already been drawn on previous passes, and establish graphics elements that will persist for the lifetime of the Gtk::DrawingArea
widget. Obviously using show_all()
or queue_draw()
and doing it all in the on_draw()
event is not the way to go.
In general, you must draw the entire widget and Cairo will clip the drawing to the predefined dirty region. See also GTK reference manual for the "GtkWidget::draw" signal for performance tips:
The signal handler will get a cr with a clip region already set to the widget's dirty region, i.e. to the area that needs repainting. Complicated widgets that want to avoid redrawing themselves completely can get the full extents of the clip region with gdk_cairo_get_clip_rectangle(), or they can get a finer-grained representation of the dirty region with cairo_copy_clip_rectangle_list().
So you may be able to redraw only the region you want with gtk_widget_queue_draw_area().