Progress Bar Gtk3+

Da li ste videli razliku između Gtk2 i Gtk3 ProgressBar-a?
Gtk3 više ne podržava tekst unutar Progresa.
I dalje je moguće da se postigne ovakav efekat ali potrebno je malo žongliranja.
Otišao sam korak dalje i ubacio sam animiranu gif sliku vlada
Pozajmio sam Vladinu profilnu i napravio animaciju na ovom sajtu https://www.kapwing.com
Priprema:

apt install gcc libgtk-3-dev

#libgtk-3-dev verzije bar 3.24, možda 3.22 nisam testirao

Sačuvajte ovaj fajl kao vlada-bar.c i sliku kao vlada.gif vlada u isti folder.

#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib.h>

#define ANIMATION "vlada.gif"

/*  Vladin bar animated progressbar
                                
    gcc -Wall -Wextra -o vlada-bar vlada-bar.c `pkg-config --cflags --libs gtk+-3.0`

                                 depends: gcc libgtk-3-dev (3.24)
*/

guint threadID = 0;

typedef struct {
    GtkWidget           *progress_bar;
    GtkWidget           *button1;
    GtkWidget           *progress_animation;
    GtkWidget           *progress_label;
    GtkStyleProvider    *progressbar_style_provider;
} app_widgets;


void destroy_handler (GtkApplication* app, gpointer data)
{
    (void) app;
    g_application_quit(G_APPLICATION (data));
}


void
stop_progress_cb (gpointer user_data)
{
  app_widgets *widgets = (app_widgets *) user_data;
  gdouble fraction;

  fraction = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR(widgets->progress_bar));
  g_print("Akcija: %.0f %%\n", fraction*100);
}


static gboolean
fill (gpointer  user_data)
{
  app_widgets *widgets = (app_widgets *) user_data;
  GtkAllocation *alloc = g_new(GtkAllocation, 1);
  gdouble fraction;
  
  /*Get the current progress*/
  fraction = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR(widgets->progress_bar));

   if (fraction > 0.999) {
      fraction = 0;
   }

  /*Increase the bar by 1% each time this function is called*/
  if (fraction < 0.999)
    fraction += 0.01;

  gtk_widget_get_allocation (widgets->progress_bar, alloc);
  
  gtk_widget_set_margin_start(widgets->progress_animation,
                          alloc->width*fraction);

  /*Fill in the bar with the new fraction*/
  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widgets->progress_bar),
                                fraction);
  gchar temp[256];
  memset(&temp, 0x0, 256);
  if (fraction > 0.999) {
     snprintf(temp, 255, "Gotovo: %.0f %%", fraction*100);

     gtk_button_set_label (GTK_BUTTON(widgets->button1),
                          "Ponovi");
     threadID = 0;
   }
   else {
     snprintf(temp, 255, "Akcija: %.0f %%", fraction*100);
   }
  gtk_label_set_text (GTK_LABEL(widgets->progress_label), temp);

  g_free(alloc);

  /*Ensures that the fraction stays below 1.0*/
  if (fraction < 0.999)
    return TRUE;

  return FALSE;
}


void
button1_clicked_cb (GtkButton *button,
                    app_widgets *widgets)
{
    if (threadID == 0)
    {
        threadID = g_timeout_add_full (0, 100, fill,
                      widgets, stop_progress_cb);
        gtk_button_set_label (button,
                      "\xe2\x8f\xb8");
    }
    else
    {
        GSource *source = g_main_context_find_source_by_id(NULL,
                                                       threadID);
         if (source)
         {
            g_source_destroy (source);
         }
         threadID = 0;
         gtk_button_set_label (button,
                      "\xe2\x8f\xaf");
    }
}

static void
progress_bar_size_allocate (GtkWidget       *progress_bar,
                              GdkRectangle    *allocation,
                              gpointer         user_data)
{
  (void) progress_bar;
  app_widgets *widgets = (app_widgets *) user_data;

  gdouble fraction;
  
  /*Get the current progress*/
  fraction = gtk_progress_bar_get_fraction
              (GTK_PROGRESS_BAR(widgets->progress_bar));
  if (fraction == 0)
  { 
     fraction = 0.01;
  }
  /*Set the margin of animation when the window width changes*/
  gtk_widget_set_margin_start(widgets->progress_animation,
                               allocation->width*fraction);
  if (fraction > 0.999)
     gtk_widget_set_margin_start(widgets->progress_animation,
                                                           1);
}


static void
progress_animation_size_allocate (GtkWidget   *animation,
                                  GdkRectangle    *allocation,
                                  gpointer         user_data)
{
  (void) animation;
  app_widgets   *widgets = (app_widgets *) user_data;
  GtkAllocation *progress_bar_allocation = g_new(GtkAllocation, 1);
  char          *css_text;

  /*Get progress bar allocation*/
  gtk_widget_get_allocation (widgets->progress_bar,
                             progress_bar_allocation);
  /*weird but working*/
  if ((progress_bar_allocation->height = allocation->height) ||
      (progress_bar_allocation->height < allocation->height))
  {
     css_text = g_strdup_printf ("progressbar trough,\n"
                                 "progressbar progress\n"
                                 "{\n"
                                 "  min-height: %dpx;\n"
                                 "}\n",
                                 allocation->height);

     gtk_css_provider_load_from_data (GTK_CSS_PROVIDER
                                      (widgets->progressbar_style_provider),
                                      css_text, -1, NULL);

    }

  g_free(progress_bar_allocation);
  g_free (css_text);
}

static void
activate (GtkApplication *app,
          gpointer        user_data)
{
  (void) user_data;
  GtkSizeGroup    *size_group;
  GtkWidget       *window;
  GtkWidget       *grid;
  GtkWidget       *button2;
  GtkWidget       *progress_overlay;
  GdkPixbufAnimation *animation;
  GError      *error = NULL;
  GtkWidget       *box;

  app_widgets *widgets = g_slice_new(app_widgets);

  gdouble fraction = 0.0;

  /*Create a window with a title, and a default size*/
  window = gtk_application_window_new (app);
  gtk_window_set_title (GTK_WINDOW (window),
                               "Vladimir Prži Gitaru");
  gtk_window_set_default_size (GTK_WINDOW (window),
                               420, 60);
  /*Create a grid container to store the widgets*/
  grid = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (grid),
                                 10);
  gtk_grid_set_column_spacing (GTK_GRID (grid),
                                 10);
  gtk_grid_set_column_homogeneous (GTK_GRID (grid),
                                TRUE);
  gtk_grid_set_row_homogeneous (GTK_GRID (grid),
                                FALSE);
  /*Create a progressbar*/
  widgets->progress_bar = gtk_progress_bar_new();
  gtk_progress_bar_set_inverted (GTK_PROGRESS_BAR(widgets->progress_bar),
                                 FALSE);
  gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR(widgets->progress_bar),
                                 FALSE);            
  /*Fill in the given fraction of the bar.
    It has to be between 0.0-1.0 inclusive*/
  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (widgets->progress_bar), fraction);


  widgets->progress_label = gtk_label_new ("Vlada je spreman");
  /*Create buttons*/
  widgets->button1 = gtk_button_new_with_label ("\xe2\x96\xb6");
  button2 = gtk_button_new_with_label ("Cancel");

  /*Create an overlay*/
  progress_overlay = gtk_overlay_new ();
  gtk_widget_set_hexpand (progress_overlay, TRUE);
  gtk_widget_set_vexpand (progress_overlay, FALSE);
  gtk_container_add (GTK_CONTAINER (progress_overlay),
                     widgets->progress_bar);
  /*Create an animation pixbuf*/
  animation = gdk_pixbuf_animation_new_from_file(ANIMATION, &error);
  if (error) {
      g_warning("No image found\n*ERROR %s\n", error->message);
      destroy_handler(NULL, app);
  }
  widgets->progress_animation = gtk_image_new_from_animation (animation);
  gtk_widget_set_vexpand(widgets->progress_bar, FALSE);
  gtk_widget_set_name (widgets->progress_animation,
                         "progress-animation");

   /*Image has to be in a (box) container with a one pixel padding*/
     
  /*create a box container for the image*/
  box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  gtk_box_pack_start (GTK_BOX(box), widgets->progress_animation, FALSE, FALSE, 1); // this one pixel is essential
  gtk_widget_set_halign (widgets->progress_animation,
                        GTK_ALIGN_START);
  gtk_overlay_add_overlay (GTK_OVERLAY (progress_overlay),
                           box);
  gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (progress_overlay),
                           box, TRUE);
  /*Size group and add widgets*/
  size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
  gtk_size_group_add_widget (size_group, widgets->progress_bar);
  gtk_size_group_add_widget (size_group, box);

  /*Create a style provider for the progessbar*/
  widgets->progressbar_style_provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ());
  gtk_style_context_add_provider (gtk_widget_get_style_context (widgets->progress_bar),
                                  widgets->progressbar_style_provider,
                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  g_signal_connect (widgets->progress_animation,
                    "size-allocate",
                    G_CALLBACK (progress_animation_size_allocate),
                     widgets);
  g_signal_connect (widgets->progress_bar,
                    "size-allocate",
                    G_CALLBACK (progress_bar_size_allocate),
                     widgets);
  gtk_grid_attach (GTK_GRID (grid), progress_overlay, 0, 0, 4, 1);
  gtk_grid_attach (GTK_GRID (grid), widgets->progress_label, 0, 1, 2, 1);
  gtk_grid_attach (GTK_GRID (grid), widgets->button1, 2, 1, 1, 1);
  gtk_grid_attach_next_to (GTK_GRID (grid), button2, widgets->button1,  GTK_POS_RIGHT, 1, 1);

  gtk_container_add (GTK_CONTAINER (window), grid);
  gtk_container_set_border_width(GTK_CONTAINER(grid),12);
  gtk_container_set_border_width(GTK_CONTAINER(window),5);
  
  g_signal_connect (G_OBJECT(window), "destroy",
                    G_CALLBACK (destroy_handler), app);
  g_signal_connect (widgets->button1, "clicked",
                    G_CALLBACK (button1_clicked_cb), widgets);
  g_signal_connect (button2, "clicked",
                    G_CALLBACK (destroy_handler), app);

  gtk_widget_show_all (window);
}


int
main (int argc, char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.vladimir_bar", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}

Sada, u istom folderu, kompajlirajte sledećom komandom:

gcc -Wall -Wextra -o vlada-bar vlada-bar.c $(pkg-config --cflags --libs gtk+-3.0)

zatim pokrenite aplikaciju u istom folderu zbog slike.

./vlada-bar

Ovo ste dobilli :slight_smile:
vlada-prži
Inače ne mora da bude gif može i png ili svg.
Ima i callback funkciju stop_progress_cb koju zove kada se pauzira peščani sat: ispisuje procenat u terminalu.

3 Likes

Nisam radio nista profesionalno u GTK. Linux uglavnom tekstualne aplikacije. Nesto sam pisao wrappere za GTK, ali progress me nije do sada zanimao :stuck_out_tongue:

hahaha dobra fora :smiley:

Nego programirati ovako GUI je tiha patnja, toliko teksta za jedan progres bar :wink: