Dibujar un gráfico con cairo en GTK3

imagen

Una matriz de tipo flotante actúa como entrada. El programa organiza la visualización, el estiramiento y el desplazamiento del gráfico.

El estilo de escritura es C con clases (sin gtkmm). Resultó no perfecto, con fluidas abstracciones. En particular, las funciones de devolución de llamada degradan la encapsulación, una parte importante de las variables debe trasladarse a la sección pública.
Básicamente, las funciones de devolución de llamada se pueden colocar en un archivo junto con el resto de las funciones de la clase, a las que llamé graphic_parameters. En GTK, cada tipo de widget tiene sus propias señales, algunas de ellas se heredan. Por ejemplo, GtkEventBox tiene una señal de "evento de presionar botón", pero no tiene el "evento de configuración" requerido para responder al cambio de tamaño del widget, ya que GtkEventBox siempre toma el tamaño del contenido. Y el tamaño del contenido se establece a mano. Puede usar el contenedor GtkFrame.

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

En cairo_t, se crean líneas, etiquetas que se muestran mediante la función cairo_stroke. Al generar el perfil, resultó que cairo_stroke requiere mucho tiempo del procesador, por lo que debe usarse lo menos posible, y el tiempo de ejecución para funciones
como cairo_move_to, cairo_line_to es bastante pequeño. Después de cairo_stroke, el contenido de cairo_t se borra y llamar a cairo_stroke (cr) nuevamente no generará nada. Puede usar
cairo_stroke_preserve para guardar el contenido y cairo_save / cairo_restore, pero no los utilicé.

Si se cambian las dimensiones (estirando con el mouse, la señal configure_event_cb), entonces para cada dibujo es necesario eliminar y recrear cairo_surface_t y cairo_t. Si rebobina el cronograma, no hay necesidad de volver a crear

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

A continuación, cairo_surface_t se traduce en una imagen.

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

Esta imagen se inserta de la siguiente manera

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

Eliminé los prefijos, es decir, scrolledwindow es del tipo GtkScrolledWindow.
Orden de adjuntos brevemente imagen-> ventana desplazada-> ventana gráfica-> caja-> caja de evento -> (Marco)
Si elimina la ventana desplazada-> contenedores de ventana gráfica, el gráfico solo aumentará, pero no disminuirá. cuadro agrega desplazamiento. Puede notar que hay 3, pero 2 de ellos no se usan y solo se necesitan para inicializar los contenedores necesarios. En los widgets de contenedor donde cabe 1 widget secundario, la función gtk_container_add se usa para insertar. g_object_set establece propiedades adicionales, en particular la falta de barras de desplazamiento para el widget de ventana desplazada
. También puede establecer propiedades a través de 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 desplazamiento: todo el segmento se divide por 100, y el cambio de horario se calcula en la función de devolución de llamada. 100 se toma de los números gtk_adjustment_new (0,0,110,1,5,10) como 100 = 110-10.

Además, sobre la parametrización.

Para parametrizar texto, utilizamos la biblioteca pango para parametrizar etiquetas. Le permite calcular el tamaño del texto en píxeles para una fuente determinada y su tamaño topográfico y exportarlo a la capa de El 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 puede ver, el pango cuenta los tamaños en sus propias unidades. Destaqué una clase separada
para el texto y sus 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);
};

Los parámetros del gráfico forman una clase 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 clase se une a la clase de aplicación principal.
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();
};

Es decir, la clase graphic_parameters se crea cuando se inicia la aplicación, y el contenido se inicializa según sea necesario al verificar NULL, 0.

La principal dificultad fue depurar todas las transformaciones. Las fallas seguras ocurrieron 3 veces: 2 veces perdidas devuelven FALSO en las funciones de devolución de llamada y no configuraron la verificación de salida del conjunto en la función de redibujo. Qt tiene una clase QCustomPlot lista para trazar, tiene muchas más posibilidades.

Enlace a github


All Articles