Desenhando um gráfico com cairo no GTK3

imagem

Uma matriz do tipo float atua como entrada. O programa organiza a exibição, alongamento, rolagem do gráfico.

O estilo de escrita é C com classes (sem gtkmm). Acabou não sendo perfeito, com abstrações fluidas. Em particular, as funções de retorno de chamada degradam o encapsulamento; uma parte significativa das variáveis ​​deve ser movida para a seção pública.
Basicamente, as funções de retorno de chamada podem ser colocadas em um arquivo junto com o restante das funções da classe, que chamei de graphic_parameters. No GTK, cada tipo de widget possui seus próprios sinais, alguns deles são herdados. Por exemplo, o GtkEventBox possui um sinal de "pressionar o botão", mas não possui o "evento de configuração" necessário para responder ao redimensionamento do widget, pois o GtkEventBox sempre aceita o tamanho do conteúdo. E o tamanho do conteúdo é definido manualmente. Você pode usar o contêiner GtkFrame.

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

No cairo_t, são criadas linhas, rótulos que são exibidos pela função cairo_stroke. Ao criar um perfil, o cairo_stroke leva muito tempo do processador, portanto deve ser usado o mínimo possível e o tempo de execução de funções
como cairo_move_to, cairo_line_to é bastante pequeno. Após cairo_stroke, o conteúdo de cairo_t é limpo e chamar cairo_stroke (cr) novamente não produz nada. Você pode usar
cairo_stroke_preserve para salvar o conteúdo e cairo_save / cairo_restore, mas eu não os usei.

Se as dimensões forem alteradas (esticando com o mouse, o sinal configure_event_cb), para cada desenho é necessário excluir e recriar cairo_surface_t e cairo_t. Se você retroceder a programação, não será necessário recriar

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

Em seguida, cairo_surface_t é traduzido em uma imagem

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

Esta imagem é então inserida da seguinte forma

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);

Eu removi os prefixos, ou seja, por exemplo, scrolledwindow é do tipo GtkScrolledWindow.
Ordem de anexo brevemente imagem-> scrolledwindow-> viewport-> box-> eventbox -> (Frame)
Se você remover os scrolledwindow-> viewport containers, o gráfico só aumentará, mas não diminuirá. caixa adiciona rolagem. Você pode perceber que existem 3, mas 2 deles não são usados ​​e são necessários apenas para inicializar os contêineres necessários. Nos widgets de contêiner nos quais um widget filho se encaixa, a função gtk_container_add é usada para inserir. g_object_set define propriedades adicionais, em particular a ausência de barras de rolagem no widget scrolledwindow
. Você também pode definir propriedades através do 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);

Mecanismo de rolagem: o segmento inteiro é dividido por 100 e a alteração de agendamento é calculada na função de retorno de chamada. 100 é obtido dos números gtk_adjustment_new (0,0,110,1,5,10) como 100 = 110-10.

Além disso, sobre parametrização.

Para parametrizar o texto, usamos a biblioteca pango para parametrizar rótulos. Permite calcular o tamanho do texto em pixels para uma determinada fonte e seu tamanho topográfico e exportá-lo para a camada 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;
}

Como você pode ver, o pango conta os tamanhos em suas próprias unidades. Eu destaquei uma classe separada
para o texto e seus parâmetros.

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);
};

Os parâmetros do gráfico formam uma classe separada:

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);
};

Esta classe ingressa na classe principal do aplicativo.
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();
};

Ou seja, a classe graphic_parameters é criada quando o aplicativo é iniciado e o conteúdo é inicializado conforme necessário, verificando NULL, 0.

A principal dificuldade foi depurar todas as transformações. Segfaults ocorreram três vezes: duas vezes, o retorno FALSE foi perdido nas funções de retorno de chamada e não definiu a verificação de saída da matriz na função redesenhar. O Qt possui uma classe QCustomPlot pronta para plotagem, possui muito mais possibilidades.

Link para o github


All Articles