繁体   English   中英

如何使用滚动条在 GTK3 区域中绘制

[英]How to draw in a GTK3 area with scroll bars

我正在构建一个嵌套的 GTK 应用程序:有 GtkNotebook - GtkGrid - GtkGrid - GtkFrame - GtkDrawingArea。

在 GtkDrawingArea 中(我通过尺寸请求固定尺寸)我创建了图纸,并且我已经建立了通过滚轮进行缩放的可能性。

从逻辑上讲,当我“放大”时,表面会变大,并且需要滚动条来查看整个绘图。

我怎样才能得到这些? 我尝试了 GtkLayout 和 GtkSrolledWindow 的不同组合,但它们没有用。 我在 Internet 上没有找到任何有用的示例。

获得可滚动绘图区域的方法是什么? 我应该从哪里开始挖掘? 任何提示都非常感谢!

谢谢你。

顺便说一句:我正在使用 Debian、GTK3、C 和开罗

MCVE:我创建了这个例子。 它绘制一个矩形,您可以使用滚轮对其进行缩放。 这发生在 300x300 帧内。 我想要这个带有滚动条的框架。 任何想法如何做到这一点?

此代码可以使用编译

gcc `pkg-config --cflags gtk+-3.0` -lm -o cf cf.c `pkg-config --libs gtk+-3.0`

参见c:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <gtk/gtk.h>

#define ZOOMING_STEP 1.2

typedef struct m_data
{
    double scaling_factor;
    cairo_surface_t *circle_surface_p, *final_surface_p;
    GtkWidget *window_p;
} m_data_struct;

static void activate(GtkApplication *, gpointer);
static gboolean zoom_it(GtkWidget *, GdkEvent *, gpointer);
static gboolean configure_it(GtkWidget *, GdkEventConfigure *, gpointer);
static gboolean draw_it(GtkWidget *, cairo_t *, gpointer);


int main(int argc, char **argv)
{
    GtkApplication *app_p;
    m_data_struct my_data;
    int status;

    my_data.scaling_factor = 1.0/11.0;
    my_data.final_surface_p = NULL;

    app_p = gtk_application_new("calc.foil", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app_p, "activate", G_CALLBACK(activate), &my_data);
    status = g_application_run(G_APPLICATION(app_p), 0, NULL);
    g_object_unref(app_p);
}


static void activate(GtkApplication *app_p, gpointer g_data_p)
{
    GtkWidget *frame_p, *notebook_p, *grid0_p, *grid1_p, *drawing_area_p;
    m_data_struct *my_data_p;
    cairo_t *cr_p;

    my_data_p = (m_data_struct *)g_data_p;

    my_data_p->window_p = gtk_application_window_new(app_p);
    gtk_window_set_title(GTK_WINDOW(my_data_p->window_p), "Fot Calculation");

    gtk_container_set_border_width(GTK_CONTAINER(my_data_p->window_p), 8);

    notebook_p = gtk_notebook_new();
    gtk_container_add(GTK_CONTAINER(my_data_p->window_p), notebook_p);

    grid0_p = gtk_grid_new();
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook_p), grid0_p, 
                 gtk_label_new("First Tab"));

    grid1_p = gtk_grid_new();
    gtk_grid_attach(GTK_GRID(grid0_p), grid1_p, 2, 2, 2, 50);

    frame_p = gtk_frame_new("Rectangle");
    drawing_area_p = gtk_drawing_area_new();
    gtk_widget_set_size_request(frame_p, 300, 300);
    g_signal_connect(drawing_area_p, "configure-event",
             G_CALLBACK(configure_it), g_data_p);
    g_signal_connect(drawing_area_p, "draw", G_CALLBACK(draw_it), g_data_p);
    g_signal_connect(drawing_area_p, "scroll-event", G_CALLBACK(zoom_it),
             g_data_p);
    gtk_widget_set_events(drawing_area_p,
              gtk_widget_get_events(drawing_area_p)
              | GDK_SCROLL_MASK);

    gtk_container_add(GTK_CONTAINER(frame_p), drawing_area_p);
    gtk_grid_attach(GTK_GRID(grid1_p), frame_p, 1, 0, 1, 2);
    my_data_p->circle_surface_p 
    = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 3000, 3000);
    cr_p = cairo_create(my_data_p->circle_surface_p);
    cairo_set_line_width(cr_p, 10);
    cairo_rectangle(cr_p, 100, 100, 2900, 2900);
    cairo_set_source_rgb(cr_p, 0, 0, 0);
    cairo_stroke(cr_p);

    gtk_widget_show_all(my_data_p->window_p);
}


static gboolean zoom_it(GtkWidget *widget_p,
            GdkEvent *event_p,
            gpointer g_data_p)
{
    if (((GdkEventScroll *)event_p)->direction == GDK_SCROLL_UP)
    {
    ((m_data_struct *)g_data_p)->scaling_factor *= ZOOMING_STEP;

    configure_it(widget_p, (GdkEventConfigure *)event_p, g_data_p);
    gtk_widget_queue_draw(widget_p);
    }
    if (((GdkEventScroll *)event_p)->direction == GDK_SCROLL_DOWN)
    {
    ((m_data_struct *)g_data_p)->scaling_factor /= ZOOMING_STEP;

    configure_it(widget_p, (GdkEventConfigure *)event_p, g_data_p);
    gtk_widget_queue_draw(widget_p);
    }

    return TRUE;
}


static gboolean configure_it(GtkWidget *widget_p,
                 GdkEventConfigure *event_p,
                 gpointer g_data_p)
{
    cairo_t *cr_p;
    m_data_struct * my_data_p;

    my_data_p = (m_data_struct *)g_data_p;

    if (my_data_p->final_surface_p)
    cairo_surface_destroy(my_data_p->final_surface_p);
    my_data_p->final_surface_p
    = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 300, 300);

    cr_p = cairo_create(my_data_p->final_surface_p);
    cairo_scale(cr_p, my_data_p->scaling_factor, my_data_p->scaling_factor);
    cairo_set_source_surface(cr_p, my_data_p->circle_surface_p, 0, 0);
    cairo_paint(cr_p);
    cairo_destroy(cr_p);

    return TRUE;
}


static gboolean draw_it(GtkWidget *widget_p,
            cairo_t *cr_p,
            gpointer g_data_p)
{
    cairo_set_source_surface(cr_p, ((m_data_struct *)g_data_p)->final_surface_p,
                 0, 0);
    cairo_paint(cr_p);

    return FALSE;
}

要生成可滚动的 GTK 绘图区域 (GtkDrawingArea),可以采用滚动的 window (GtkScrolledWindow)。 绘图区域是滚动的 window (gtk_container_add) 的子项。

但是,要滚动绘图区域,我需要在每次更改它时指定它的大小(gtk_widget_set_size_request)。 当然,只有当它的大小大于滚动的 window 的大小时,滚动才有意义。

最后我想用滚轮放大我的开罗画。 这仅在鼠标下的点固定时才有效。 所以我需要调整滚动的 window (GtkAdjustment) 不让绘图在鼠标下移动。 除非缩小到非常低的水平。

但是,当绘图区域发生更改时,我无法找到这些调整的设置位置,因此我必须在放大时手动进行。

我以现在可以正常工作的方式更改了示例。 代码运行完成:cf.c:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <gtk/gtk.h>

#define ZOOMING_STEP 1.2
#define SCALING_FACTOR_INIT 1.0/10.0

typedef struct m_data
{
    double scaling_factor;
    cairo_surface_t *rectangle_surface_p, *final_surface_p;
    GtkWidget *window_p, *drawing_area_p;
    GtkAdjustment *hadjust_p, *vadjust_p;
} m_data_struct;

static void activate(GtkApplication *, gpointer);
static gboolean zoom_it(GtkWidget *, GdkEvent *, gpointer);
static gboolean configure_it(GtkWidget *, GdkEventConfigure *, gpointer);
static gboolean draw_it(GtkWidget *, cairo_t *, gpointer);


int main(int argc, char **argv)
{
    GtkApplication *app_p;
    m_data_struct my_data;
    int status;

    my_data.scaling_factor = SCALING_FACTOR_INIT;
    my_data.final_surface_p = NULL;

    app_p = gtk_application_new("calc.foil", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app_p, "activate", G_CALLBACK(activate), &my_data);
    status = g_application_run(G_APPLICATION(app_p), 0, NULL);
    g_object_unref(app_p);
}


static void activate(GtkApplication *app_p, gpointer g_data_p)
{
    GtkWidget *frame_p, *notebook_p, *grid0_p, *grid1_p, *drawing_area_p;
    GtkWidget *scrolled_window_p, *frame0_p;
    m_data_struct *my_data_p;
    cairo_t *cr_p;

    my_data_p = (m_data_struct *)g_data_p;

    my_data_p->window_p = gtk_application_window_new(app_p);
    gtk_window_set_title(GTK_WINDOW(my_data_p->window_p), "Fot Calculation");

    gtk_container_set_border_width(GTK_CONTAINER(my_data_p->window_p), 8);

    notebook_p = gtk_notebook_new();
    gtk_container_add(GTK_CONTAINER(my_data_p->window_p), notebook_p);

    grid0_p = gtk_grid_new();
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook_p), grid0_p, 
                             gtk_label_new("First Tab"));

    grid1_p = gtk_grid_new();
    gtk_grid_attach(GTK_GRID(grid0_p), grid1_p, 2, 2, 2, 50);

    frame_p = gtk_frame_new("Rectangle");
    gtk_frame_set_shadow_type(GTK_FRAME(frame_p), GTK_SHADOW_NONE);
    frame0_p = gtk_frame_new(NULL);
    scrolled_window_p = gtk_scrolled_window_new(NULL, NULL);
    my_data_p->hadjust_p
    = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW
                                          (scrolled_window_p));
    my_data_p->vadjust_p
    = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW
                                          (scrolled_window_p));
    drawing_area_p = gtk_drawing_area_new();
    gtk_widget_set_size_request(scrolled_window_p, 300, 300);
    g_signal_connect(drawing_area_p, "configure-event",
                     G_CALLBACK(configure_it), g_data_p);
    g_signal_connect(drawing_area_p, "draw", G_CALLBACK(draw_it), g_data_p);
    g_signal_connect(drawing_area_p, "scroll-event", G_CALLBACK(zoom_it),
                     g_data_p);
    gtk_widget_set_events(drawing_area_p,
                          gtk_widget_get_events(drawing_area_p)
                          | GDK_SCROLL_MASK);
    my_data_p->drawing_area_p = drawing_area_p;

    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_p),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_container_add(GTK_CONTAINER(scrolled_window_p), drawing_area_p);
    gtk_container_add(GTK_CONTAINER(frame_p), frame0_p);
    gtk_container_add(GTK_CONTAINER(frame0_p), scrolled_window_p);
    gtk_grid_attach(GTK_GRID(grid1_p), frame_p, 1, 0, 1, 2);
    my_data_p->rectangle_surface_p 
        = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 3000, 3000);
    cr_p = cairo_create(my_data_p->rectangle_surface_p);
    cairo_set_line_width(cr_p, 50);
    cairo_rectangle(cr_p, 100, 100, 1375, 1375);
    cairo_rectangle(cr_p, 1525, 1525, 1375, 1375);
    cairo_set_source_rgb(cr_p, 0, 0, 0);
    cairo_stroke(cr_p);

    gtk_widget_show_all(my_data_p->window_p);
}


static gboolean zoom_it(GtkWidget *widget_p,
                        GdkEvent *event_p,
                        gpointer g_data_p)
{
    m_data_struct *my_data_p;
    GdkEventScroll *this_event_p;
    gdouble new_x, new_y;
    int do_zoom = 0;

    my_data_p = (m_data_struct *)g_data_p;
    this_event_p = (GdkEventScroll *)event_p;

    if (this_event_p->direction == GDK_SCROLL_UP)
    {
        my_data_p->scaling_factor *= ZOOMING_STEP;

        gtk_adjustment_set_upper(my_data_p->hadjust_p,
                                 gtk_adjustment_get_upper(my_data_p->hadjust_p)
                                 * ZOOMING_STEP
                                 + 1);       /* we need to calc the new upper
                                                to set value, +1 for 
                                                inaccuracy */
        gtk_adjustment_set_upper(my_data_p->vadjust_p,
                                 gtk_adjustment_get_upper(my_data_p->vadjust_p)
                                 * ZOOMING_STEP
                                 + 1);     
        new_x = this_event_p->x * ZOOMING_STEP;
        new_y = this_event_p->y * ZOOMING_STEP;

        do_zoom = 1;
    }

    if (this_event_p->direction == GDK_SCROLL_DOWN)
    {
        double sf;

        sf = my_data_p->scaling_factor / ZOOMING_STEP;

        if (sf >= SCALING_FACTOR_INIT / (1 + (ZOOMING_STEP - 1) / 2))
                                             /* zoom out max till level 0 but
                                                preventing inability not to zoom
                                                to level 0 due to inaccurancy */
        {
            my_data_p->scaling_factor = sf;

            new_x = this_event_p->x / ZOOMING_STEP;
            new_y = this_event_p->y / ZOOMING_STEP;

            do_zoom = 1;
        }
    }

    if (do_zoom)
    {
        gtk_adjustment_set_value
            (my_data_p->hadjust_p,
             new_x
             + gtk_adjustment_get_value(my_data_p->hadjust_p)
             - this_event_p->x);
        gtk_adjustment_set_value
            (my_data_p->vadjust_p,
             new_y
             + gtk_adjustment_get_value(my_data_p->vadjust_p)
             - this_event_p->y);

        configure_it(widget_p, (GdkEventConfigure *)event_p, g_data_p);
        gtk_widget_queue_draw(widget_p);
    }

    return TRUE;
}


static gboolean configure_it(GtkWidget *widget_p,
                             GdkEventConfigure *event_p,
                             gpointer g_data_p)
{
    cairo_t *cr_p;
    m_data_struct *my_data_p;

    my_data_p = (m_data_struct *)g_data_p;

    if (my_data_p->final_surface_p)
        cairo_surface_destroy(my_data_p->final_surface_p);
    my_data_p->final_surface_p
        = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
                                     3000 * my_data_p->scaling_factor,
                                     3000 * my_data_p->scaling_factor);
    gtk_widget_set_size_request(my_data_p->drawing_area_p,
                                3000 * my_data_p->scaling_factor,
                                3000 * my_data_p->scaling_factor);

    cr_p = cairo_create(my_data_p->final_surface_p);
    cairo_scale(cr_p, my_data_p->scaling_factor, my_data_p->scaling_factor);
    cairo_set_source_surface(cr_p, my_data_p->rectangle_surface_p, 0, 0);
    cairo_paint(cr_p);
    cairo_destroy(cr_p);

    return TRUE;
}


static gboolean draw_it(GtkWidget *widget_p,
                        cairo_t *cr_p,
                        gpointer g_data_p)
{
    cairo_set_source_surface(cr_p, ((m_data_struct *)g_data_p)->final_surface_p,
                             0, 0);
    cairo_paint(cr_p);

    return FALSE;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM