Menggambar grafik dengan cairo di GTK3

gambar

Array tipe float bertindak sebagai input. Program ini mengatur tampilan, peregangan, pengguliran grafik.

Gaya penulisan adalah C dengan kelas (tanpa gtkmm). Ternyata tidak sempurna, dengan abstraksi yang mengalir. Secara khusus, fungsi callback menurunkan enkapsulasi, sebagian besar variabel harus dipindahkan ke bagian publik.
Pada dasarnya, fungsi panggilan balik dapat ditempatkan dalam file bersama dengan sisa fungsi kelas, yang saya namakan graphic_parameters. Di GTK, setiap jenis widget memiliki sinyal sendiri, beberapa di antaranya diwariskan. Misalnya, GtkEventBox memiliki sinyal "tombol-tekan-peristiwa", tetapi tidak memiliki "acara-konfigurasi" yang diperlukan untuk merespons pengubahan ukuran widget, karena GtkEventBox selalu mengambil ukuran konten. Dan ukuran konten diatur dengan tangan. Anda bisa menggunakan wadah GtkFrame.

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

Di cairo_t, baris, label dibuat yang ditampilkan oleh fungsi cairo_stroke. Saat membuat profil, ternyata cairo_stroke membutuhkan banyak waktu prosesor, sehingga harus digunakan sesedikit mungkin, dan waktu eksekusi untuk fungsi
seperti cairo_move_to, cairo_line_to cukup kecil. Setelah cairo_stroke, isi cairo_t dihapus dan memanggil cairo_stroke (cr) lagi tidak akan menghasilkan apa-apa. Anda dapat menggunakan
cairo_stroke_preserve untuk menyimpan konten dan cairo_save / cairo_restore, tetapi saya tidak menggunakannya.

Jika dimensi diubah (dengan meregangkan dengan mouse, sinyal configure_event_cb), maka untuk setiap gambar perlu menghapus dan membuat ulang cairo_surface_t dan cairo_t. Jika Anda memundurkan jadwal, maka tidak perlu membuat ulang

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

Selanjutnya, cairo_surface_t diterjemahkan ke dalam gambar

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

Gambar ini kemudian disisipkan sebagai berikut

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

Saya menghapus awalan, yaitu, misalnya, scrolledwindow adalah tipe GtkScrolledWindow.
Agar lampiran singkat Image-> scrolledwindow-> viewport-> kotak-> eventbox -> (Frame)
Jika Anda menghapus kontainer scrolledwindow-> viewport, grafik hanya akan meningkat, tapi tidak menurun. kotak menambahkan scrolling. Anda mungkin memperhatikan bahwa ada 3, tetapi 2 dari mereka tidak digunakan dan hanya diperlukan untuk menginisialisasi wadah yang diperlukan. Dalam widget kontainer di mana 1 widget anak cocok, fungsi gtk_container_add digunakan untuk menyisipkan. g_object_set menetapkan properti tambahan, khususnya kurangnya bilah gulir untuk widget scrolledwindow
. Anda juga dapat mengatur properti melalui 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);

Mekanisme gulir: seluruh segmen dibagi 100, dan perubahan jadwal dihitung dalam fungsi panggilan balik. 100 diambil dari angka gtk_adjustment_new (0,0110,1,5,10) sebagai 100 = 110-10.

Selanjutnya, tentang parameterisasi.

Untuk parameterisasi teks, kami menggunakan pango library untuk parameter parameter label. Ini memungkinkan Anda untuk menghitung ukuran teks dalam piksel untuk font tertentu dan ukuran topografinya dan mengekspornya ke lapisan Kairo.

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

Seperti yang Anda lihat, pango mempertimbangkan ukuran dalam unitnya sendiri. Saya menyoroti kelas terpisah
untuk teks dan parameternya.

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

Parameter bagan membentuk kelas yang terpisah:

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

Kelas ini bergabung dengan kelas aplikasi utama.
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();
};

Yaitu, kelas graphic_parameters dibuat ketika aplikasi dimulai, dan konten diinisialisasi seperlunya dengan memeriksa NULL, 0.

Kesulitan utama adalah men-debug semua transformasi. Segfault terjadi 3 kali: 2 kali gagal mengembalikan FALSE dalam fungsi panggilan balik dan tidak menetapkan pemeriksaan untuk keluar dari larik dalam fungsi redraw. Qt memiliki kelas QCustomPlot yang siap pakai untuk merencanakan, ia memiliki lebih banyak kemungkinan.

Tautan ke github


All Articles