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

Я использую С++ 11 с цепочкой инструментов GNU с gtkmm3 на Ubuntu 12.04 LTS 32 бит. Я играл с некоторыми примерами для 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() — не лучший вариант.


person Vector    schedule 02.07.2014    source источник


Ответы (1)


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

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

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

person elmarco    schedule 02.07.2014
comment
Я видел документы и функции, касающиеся области клипа, грязной области и т. Д., Но не мог понять, как определяется грязная область и как ее вычислить: поскольку предыдущая draw уже закончена, не все ли чисто в следующий раз около? (Я не только новичок в Каире, я новичок в такого рода программировании в целом — я обычно занимаюсь промежуточным программным обеспечением и работой на стороне сервера). Независимо от того, что вы говорите, кажется, что у меня есть два варианта: сохранить мой предыдущий точки и каждый раз перерисовывать все линии; Рассчитать грязную область, которую мне нужно рисовать для каждого прохода, и ограничить рисование этой областью? - person Vector; 02.07.2014
comment
Грязная область определяется оконной системой, а область перерисовывается вручную с помощью queue_draw*(). Итак, да, либо вы перерисовываете все и позволяете отсечению делать за вас подъем, либо вы вычисляете и ставите в очередь свою грязную область и рисуете только в грязной области. Скорее всего можно просто все перерисовать, все равно будет достаточно быстро благодаря отсечению. - person elmarco; 03.07.2014
comment
Скорее всего, вы можете просто перерисовать все... : Это то, что я решил сделать - мне гораздо проще просто хранить ранее нарисованные линии в контейнере и повторять их, чтобы перерисовать, чем начинать выяснять как получить грязный регион и отслеживать, где я должен быть для следующей строки. Это очень простое приложение, предназначенное только для того, чтобы научиться основам, и я не ожидаю, что попаду в ситуацию, когда рисунок будет настолько расширенным и сложным, что производительность станет проблемой, особенно потому, что, как вы объяснили, отсечение сведет к минимуму удар. - person Vector; 03.07.2014