Как нарисовать новую линию в области Gtk::DrawingArea, сохраняя при этом предыдущие линии, которые уже были нарисованы?

Я использую C++11 с цепочкой инструментов GNU с gtkmm3, на Ubuntu 12.04 LTS 32 bit. Я играл с некоторыми примерами для gtkmm3 в Программирование с помощью gtkmm 3.

На основании 17.2.1. Пример там я унаследовал от Gtk::DrawingArea (MyDrawingArea здесь) и преодолеть on_draw() Обработчик событий выглядит следующим образом:

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;

    }

Моя цель - нарисовать простую гистограмму, основанную на вычислениях, которые я делаю в небольшом тестовом приложении. Идея заключается в том, чтобы каждый раз on_draw() событие вызвано, следующий бар будет перемещен на 5 единиц вправо на mXAxis и вертикальная линия будет проведена на основе нового mYaxis значение, которое рассчитывается на основе результатов нового расчета.

Когда я хочу перекрасить свой график и вызвать событие MyDrawingArea::on_draw(), я вызываю MyDrawingArea.show_all() из моего приложения после завершения расчета и установки новых осей x и y.

Тем не менее, это не работает, как я ожидал: MyDrawingArea.show_all() делает недействительным все окно рисования и рисует с нуля: новая линия графика появляется на своем месте, но предыдущие стираются. Я тоже пробовал MyDrawingArea.queue_draw(), который имел тот же эффект. Но я хочу сохранить результаты предыдущего графика, чтобы получить профиль результатов расчета, так как я вычисляю с разными значениями.

Эта реализация также вызывает нижнюю линию на моем графике (моя ось x на графике)- нарисованная первым stroke() вызов в моем примере кода, который будет отображаться заново при каждом вызове on_draw() - хотя это не должно быть необходимым, так как эта строка сохраняется в течение всей жизни MyDrawingArea - не должно быть необходимости делать недействительными, а затем перерисовывать его на каждом новом on_draw() событие, как мой код в настоящее время делает, потому что я еще не нашел способ справиться с этим.

Я очень новичок в Каире, так что я уверен, что, возможно, я делаю это совершенно неправильно, но явной, ориентированной на задачи документации, похоже, немного - я не нашел ничего, что объясняло бы, как это сделать, хотя я уверен, что это это довольно просто.

Что мне нужно сделать, чтобы нарисовать новую линию на Gtk::DrawingArea , сохраняя при этом предыдущие линии графика, которые уже были нарисованы на предыдущих проходах, и устанавливая графические элементы, которые будут сохраняться в течение всего срока действия Gtk::DrawingArea виджет. Очевидно, используя show_all() или же queue_draw() и делать все это в on_draw() Событие не путь.

1 ответ

Решение

В общем, вы должны нарисовать весь виджет, и Cairo обрежет рисунок в заранее заданную грязную область. См. Также справочное руководство по GTK для сигнала "GtkWidget::draw" для советов по повышению производительности:

Обработчик сигнала получит cr с областью клипа, уже установленной в грязную область виджета, то есть в область, которая требует перерисовки. Сложные виджеты, которые хотят избежать полного перерисовывания, могут получить полные экстенты области клипа с помощью gdk_cairo_get_clip_rectangle(), или они могут получить более детальное представление грязной области с помощью cairo_copy_clip_rectangle_list().

Таким образом, вы можете перерисовать только ту область, которую хотите, с помощью gtk_widget_queue_draw_area().

Другие вопросы по тегам