3

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.

Vector
  • 10,879
  • 12
  • 61
  • 101

1 Answers1

4

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().

elmarco
  • 31,633
  • 21
  • 64
  • 68
  • I saw the docs and functions regarding the clip region, dirty region, etc., but could not figure out how the dirty region is determined and how to calculate it: Since the previous `draw` is already finished, isn't everything clean the next time around? (I am not just new to Cairo, I am new to this sort of programming in general-I usually do middle-ware and server-side work) Regardless, from what you're saying seems that I have two choices: Persist my previous points and redraw all lines each time; Calculate the dirty area I need to draw on for each pass and restrict the drawing to that area? – Vector Jul 02 '14 at 16:45
  • The dirty region is determined by the windowing system, and the region to redraw manually with queue_draw*(). So yes, either you redraw all and let clipping do the lifting for you, or you calculate and queue your dirty region, and draw only in the dirty region. Most probably, you can just redraw all, it will be fast enough anyway thanks to clipping. – elmarco Jul 03 '14 at 10:49
  • 2
    _Most probably, you can just redraw all..._ : That's what I opted to do - much easier for me to just store previously drawn lines in a container and iterate through to redraw them, than start figuring out how to get the dirty region and keep track of where I'm supposed to be for the next line. This is a very simple application just to teach myself the basics and I don't anticipate getting into a situation where the drawing would be so extended and complex that performance would be an issue, particularly since, as you have explained, clipping will minimize the hit. – Vector Jul 03 '14 at 11:29