在GTK3中用cairo绘制图形

图片

float类型的数组充当输入。该程序组织图表的显示,拉伸,滚动。

写作风格是带有类的C(没有gtkmm)。事实证明,完美的抽象是不完美的。特别是,回调函数会降低封装性能,其中很大一部分变量必须移至公共部分。
基本上,回调函数可以与我称为graphic_parameters的类的其余函数一起放在文件中。在GTK中,每种类型的小部件都有其自己的信号,其中一些是继承的。例如,由于GtkEventBox始终采用内容的大小,因此GtkEventBox具有“ button-press-event”信号,但不具有响应小部件调整大小所需的“ configure-event”。内容的大小是手动设置的。您可以使用GtkFrame容器。

cairo_surface_t  *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_t  *cr = cairo_create(surface);

在cairo_t中,将创建由cairo_stroke函数显示的行和标签。分析时发现cairo_stroke占用大量处理器时间,因此应尽可能少地使用它,并且
cairo_move_to,cairo_line_to之类的函数的执行时间非常短。在cairo_stroke之后,cairo_t的内容将被清除,再次调用cairo_stroke(cr)将不会输出任何内容。您可以使用
cairo_stroke_preserve保存内容和cairo_save / cairo_restore,但我没有使用它们。

如果更改了尺寸(通过用鼠标拉伸configure_event_cb信号),则对于每个图形,都必须删除并重新创建cairo_surface_t和cairo_t。如果倒带时间表,则无需重新创建

    cairo_set_source_rgb(cr,0.8,0.8,0.8);
    cairo_paint(cr);

接下来,将cairo_surface_t转换为图像

void gtk_image_set_from_surface (GtkImage *image, cairo_surface_t *surface);

然后按如下方式插入此图像

eventbox=gtk_event_box_new();
    g_signal_connect(eventbox,"button-press-event", G_CALLBACK(eventbox_press_cb), this);
    GtkAdjustment *adj_h=gtk_adjustment_new(0,0,100,1,5,10);
    GtkAdjustment *adj_v=gtk_adjustment_new(0,0,100,1,5,10);
    GtkWidget *viewport=gtk_viewport_new(adj_h, adj_v);
    scrolledwindow=gtk_scrolled_window_new(adj_h, adj_v);
    g_object_set(scrolledwindow, "hscrollbar-policy", GTK_POLICY_EXTERNAL, "vscrollbar-policy", GTK_POLICY_EXTERNAL, NULL);
    gtk_container_add(GTK_CONTAINER(viewport), scrolledwindow);
    gtk_widget_set_events(scrolledwindow, GDK_SCROLL_MASK); 
    g_signal_connect(scrolledwindow,"scroll-event",G_CALLBACK(eventbox_scroll_cb), this);
    GtkWidget *box=gtk_box_new(GTK_ORIENTATION_VERTICAL,0);
    adj=gtk_adjustment_new(0,0,110,1,5,10);
    g_signal_connect(adj,"value-changed", G_CALLBACK(adj_changed_cb), this);
    scrollbar=gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL,adj);
    gtk_box_pack_end(GTK_BOX(box),scrollbar, FALSE,FALSE,0);
    image_from_surface=gtk_image_new_from_surface(surface);
    gtk_container_add(GTK_CONTAINER(scrolledwindow),image_from_surface);
    gtk_box_pack_start(GTK_BOX(box),viewport, TRUE,TRUE,0);
    gtk_container_add(GTK_CONTAINER(eventbox),box);

我删除了前缀,例如,scrolledwindow是GtkScrolledWindow类型。
附件顺序简要说明image-> scrolledwindow-> viewport-> box-> eventbox->(框架)
如果删除scrolledwindow- > viewport容器,则图形只会增加,而不会减少。框添加滚动。您可能会注意到有3个,但是其中2个没有使用,仅在初始化必要的容器时才需要。在适合1个子窗口小部件的容器窗口小部件中,使用gtk_container_add函数插入。g_object_set设置其他属性,尤其是缺少scrolledwindow小部件的滚动条
您还可以通过GValue设置属性

    GValue val = G_VALUE_INIT;
    g_value_init(&val, G_TYPE_BOOLEAN);
    g_value_set_boolean(&val, TRUE);
    gtk_container_child_set_property(GTK_CONTAINER(data->notebook), gr, "tab-expand", &val);

滚动机制:将整个段除以100,并在回调函数中计算计划更改。从数字gtk_adjustment_new(0,0,110,1,5,10)中取100作为100 = 110-10。

此外,关于参数化。

要参数化文本,我们使用pango库参数化标签。它允许您计算给定字体的文本大小(以像素为单位)及其拓扑大小,并将其导出到cairo层。

PangoLayout* get_width_height_of_text(char *text, char *font, float size, float *w, float *h)
{
    GdkScreen *screen = gdk_screen_get_default();
    PangoContext *context = gdk_pango_context_get_for_screen (screen);
    PangoLayout *layout = pango_layout_new (context);
    if(g_utf8_validate(text,-1,0))
    {
        pango_layout_set_text(layout,text,-1);
        PangoFontDescription *desc=pango_font_description_new();
        pango_font_description_set_family(desc,font);
        pango_font_description_set_size(desc,size*1024);
        pango_layout_set_font_description (layout, desc);
        int width=0,height=0;
        pango_layout_get_size(layout, &width, &height);
        *w=(float) width/1024;
        *h=(float) height/1024;
        pango_font_description_free(desc);
    }
    else
    {
        printf("      UTF8\n");
    }
    return layout;
}

如您所见,pango以自己的单位计算大小。我突出显示了
文本及其参数的单独类

class text_layout
{
    private:
    int fontsize;
    public:
    GString *text;
    GString *font;
    PangoLayout *layout;
    int width;
    int height;
    text_layout(char *text, char *font, int fontsize);
    void change_text_font_fontsize(char *new_text, char *new_font, int new_fontsize);
    ~text_layout();
    text_layout(float num, char *font, int fontsize);
};

图表参数构成一个单独的类:

class graphic_parameters
{
    private:
     text_layout y_text=text_layout(" y","Liberation Serif", 14);
     text_layout x_text=text_layout(" x","Liberation Serif", 14);
     text_layout *number=0; ///   
     float max=0;
     float min=0;
     text_layout *max_=0;
     text_layout *min_=0;

    GtkAdjustment *adj;
    GtkWidget *scrollbar;
     float gap_x=25; ///    
     float gap_y=5; ///    

    void create_axes_and_xy_labels(void);

    public:
    cairo_t *cr;
    float *massiv=0; ///  
    int len=0; /// 
    int count_in_display=0; ///   
    float multiplier_x=6;
    int offset=0;
    float x_null=0;
    float y_null=0;
    int pos=0;///  
    float margin=16;

    int callback_width;  /// 
    int callback_height;
    int widget_width;
    int widget_height;
    int scroll_height=0;
    GtkWidget *eventbox;
    GtkWidget *scrolledwindow;
    GtkWidget *image_from_surface;
    cairo_surface_t *surface;

    graphic_parameters(int width, int height);
    ~graphic_parameters();
    void resize_graphic(int new_width, int new_height);
    void create_one_dimensional_graphic(float *massiv, int size);
    void update_graphic(int offset);
    void change_graphic_adj(void);
    void create_vertical_line(void);
};

该类加入主应用程序类。
class externals
{
    public:
    graphic_parameters *param;
    externals();
};

class appdata : public externals
{
    public:
    char *glade_name=(char*)"window.glade";
    GtkApplication *app;
    GtkWidget *win;
    GtkNotebook *notebook;
    GtkMenuBar *menubar;
    appdata();
};

也就是说,在应用程序启动时将创建graphic_parameters类,并根据需要通过检查NULL,0来初始化内容。

主要困难是调试所有转换。Segfaults发生了3次:在回调函数中错失了2次返回FALSE,并且没有在redraw函数中设置从数组退出的检查。Qt有一个现成的QCustomPlot类用于绘图,它具有更多的可能性。

链接到github


All Articles