GtkApplication का उपयोग करना। Librsvg रेंडरिंग सुविधाएँ

लेख का सार।

  • GtkApplication का उपयोग करना। वायरफ्रेम अनुप्रयोग। Makefile।
  • पुस्तकालय पुस्तकालय द्वारा प्रतिपादन।
  • GtkImage में एक छवि निर्यात करें और इसे स्केल करें।
  • कस्टम सुविधाओं के साथ एसवीजी स्केलिंग।
  • अनुप्रयोगों में पूर्ण पथ प्राप्त करना।
  • प्रदर्शन परीक्षण GtkDrawingArea बनाम GtkImage।

इससे पहले GTK + हब में लेख (मेरा नहीं) थे जो उदाहरणों में शून्य gtk_main (शून्य) फ़ंक्शन का उपयोग करते हैं; GtkApplication वर्ग आपको application_activate और application_shutdown कॉलबैक फ़ंक्शन को स्पष्ट रूप से उजागर करने की अनुमति देता है। Gtk_main के साथ, आपको स्पष्ट रूप से gtk_main_quit को हुक करना होगा ताकि जब आप क्रॉस पर क्लिक करें, तो एप्लिकेशन समाप्त हो जाए। GtkApplication क्रॉस पर क्लिक करके एप्लिकेशन को समाप्त करता है, जो अधिक तार्किक है। आवेदन की रूपरेखा स्वयं में main.h, Makefile, string.gresource.xml, main.c.

main.h

#ifndef MAIN_H #define MAIN_H #include <gtk/gtk.h> typedef struct{ GtkApplication *restrict app; GtkWidget *restrict win; GtkBuilder *restrict builder; }appdata; appdata data; appdata *data_ptr; #endif 

makefile

यहां सार्वभौमिक है, यह आपको विशिष्ट फ़ाइल नामों को निर्दिष्ट किए बिना सभी स्रोत फ़ाइलों को संकलित करने की अनुमति देता है, लेकिन अगर फ़ोल्डर में अतिरिक्त फाइलें हैं, तो संकलक शपथ लेंगे।
आप CC = g ++ -std = c ++ 11 का भी उपयोग कर सकते हैं, लेकिन कॉलबैक फ़ंक्शन में डाल सकते हैं
बाहरी "सी"।

 CC = gcc -std=c99 PKGCONFIG = $(shell which pkg-config) CFLAGS = $(shell $(PKGCONFIG) --cflags gio-2.0 gtk+-3.0 librsvg-2.0) -rdynamic -O3 LIBS = $(shell $(PKGCONFIG) --libs gio-2.0 gtk+-3.0 gmodule-2.0 librsvg-2.0 epoxy) -lm GLIB_COMPILE_RESOURCES = $(shell $(PKGCONFIG) --variable=glib_compile_resources gio-2.0) SRC = $(wildcard *.c) GEN = gresources.c BIN = main ALL = $(GEN) $(SRC) OBJS = $(ALL:.c=.o) all: $(BIN) gresources.c: string.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=. --generate-dependencies string.gresource.xml) $(GLIB_COMPILE_RESOURCES) string.gresource.xml --target=$@ --sourcedir=. --generate-source %.o: %.c $(CC) $(CFLAGS) -c -o $(@F) $< $(BIN): $(OBJS) $(CC) -o $(@F) $(OBJS) $(LIBS) clean: @rm -f $(GEN) $(OBJS) $(BIN) 

string.gresource.xml

निष्पादन योग्य फ़ाइल में संसाधनों को शामिल करने के लिए कार्य करता है, इस मामले में यह एक window.glade इंटरफ़ेस विवरण फ़ाइल है

 <?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/com/example/YourApp"> <file preprocess="xml-stripblanks" compressed="true">window.glade</file> </gresource> </gresources> 

main.c

 #include "main.h" GtkBuilder* builder_init(void) { GError *error = NULL; data.builder = gtk_builder_new(); if (!gtk_builder_add_from_resource (data.builder, "/com/example/YourApp/window.glade", &error)) { //     g_critical ("   : %s", error->message); g_error_free (error); } gtk_builder_connect_signals (data.builder,NULL); return data.builder; } void application_activate(GtkApplication *application, gpointer user_data) { GtkBuilder *builder=builder_init(); data_ptr=&data; data.win=GTK_WIDGET(gtk_builder_get_object(builder, "window1")); gtk_widget_set_size_request(data.win,360,240); gtk_application_add_window(data.app,GTK_WINDOW(data.win)); gtk_widget_show_all(data.win); } void application_shutdown(GtkApplication *application, gpointer user_data) { g_object_unref(data.builder); } int main (int argc, char *argv[]) { gtk_init (&argc, &argv); gint res; data.app = gtk_application_new("gtk.org", G_APPLICATION_FLAGS_NONE); g_signal_connect(data.app, "activate", G_CALLBACK(application_activate), NULL); g_signal_connect(data.app, "shutdown", G_CALLBACK(application_shutdown), NULL); res = g_application_run(G_APPLICATION(data.app), 0, NULL); return 0; } 

Gtk_application_new फ़ंक्शन के पहले तर्क में, आप किसी भी पाठ को रख सकते हैं, लेकिन यह बिना डॉट के काम नहीं करेगा। यह उदाहरण window.glade फ़ाइल को भी छोड़ देता है, जिसे Glade UI संपादक में बनाया जा सकता है।

हम विंडो को GtkBox कंटेनर से 2 भागों में विभाजित करते हैं, उनमें से एक में हम GtkDrawingArea डालते हैं, दूसरे पर:



नतीजतन, appdata बदल जाएगा

 typedef struct{ GtkApplication *restrict app; GtkWidget *restrict win; GtkBuilder *restrict builder; GtkDrawingArea *restrict draw; GtkImage *restrict image; GtkEventBox *restrict eventbox1; RsvgHandle *restrict svg_handle_image; RsvgHandle *restrict svg_handle_svg; GdkPixbuf *pixbuf; cairo_t *restrict cr; cairo_surface_t *restrict surf; }appdata; 

और तदनुसार आरंभीकरण।

 void application_activate(GtkApplication *application, gpointer user_data) { GtkBuilder *builder=builder_init(); data_ptr=&data; data.win=GTK_WIDGET(gtk_builder_get_object(builder, "window1")); data.draw=GTK_DRAWING_AREA(gtk_builder_get_object(builder, "drawingarea1")); data.image=GTK_IMAGE(gtk_builder_get_object(builder, "image1")); gtk_widget_set_size_request(data.win,640,480); gtk_application_add_window(data.app,GTK_WINDOW(data.win)); gtk_widget_show_all(data.win); } 

पथ #include <librsvg-2.0 / librsvg / rsvg.h> जोड़ें। (Librsvg और librsvg-dev संकुल स्थापित होना चाहिए)।

कॉलबैक फ़ंक्शन नाम .glade फ़ाइल से लिए गए हैं, इसके लिए फ़ंक्शन जिम्मेदार है
gtk_builder_connect_signals (data.builder, NULL);

 gboolean drawingarea1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { if(!data.svg_handle_svg) {data.svg_handle_svg=rsvg_handle_new_from_file("compassmarkings.svg",NULL);} gboolean result=rsvg_handle_render_cairo(data.svg_handle_svg,cr); if(result&&cr) {cairo_stroke(cr);} else printf(" \n"); return FALSE; } 

कुछ स्थितियों में (जैसे एचएमआई), आपको एसवीजी का आकार बदलने की आवश्यकता हो सकती है। कर सकते हैं
एसवीजी फ़ाइल में चौड़ाई और ऊंचाई के मापदंडों को बदलें। या GtkPixbuf और वहाँ पहले से ही स्केलिंग बनाने के लिए स्थानांतरण। चूंकि GtkImage को GtkBin से विरासत में नहीं मिला है, इसलिए इसमें अपने खुद के ButtonClick प्रकार के ईवेंट (कर्सर-संबंधित ईवेंट) नहीं हो सकते हैं। इसके लिए एक खाली कंटेनर है - GtkEventBox। और ड्राइंग को सीधे GtkImage पर लटका दिया जा सकता है।

 gboolean image1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { if(!data.svg_handle_image) { data.svg_handle_image=rsvg_handle_new_from_file("compassmarkings.svg",NULL); data.surf=cairo_image_surface_create_from_png("2.png"); data.pixbuf=rsvg_handle_get_pixbuf(data.svg_handle_image); } if(data.pixbuf) { cairo_set_source_surface(cr,data.surf,0,0); GdkPixbuf *dest=gdk_pixbuf_scale_simple (data.pixbuf,250,250,GDK_INTERP_BILINEAR); gtk_image_set_from_pixbuf (data.image,dest); g_object_unref(dest); cairo_paint(cr); } } 

यह फ़ंक्शन पृष्ठभूमि छवि (2.png) को लोड करता है, जो अक्सर प्रतिनिधित्व करता है
पारदर्शी पिक्सेल के साथ 1x1 ड्राइंग। और फिर एक ड्राइंग (पिक्सबुफ) इस सतह पर प्रदान की जाती है और फिर ज़ूम करके छवि को निर्यात किया जाता है (छवि)।

और हमें स्मृति को साफ़ करने के बारे में नहीं भूलना चाहिए।

 void application_shutdown(GtkApplication *application, gpointer user_data) { cairo_surface_destroy(data.surf); g_object_unref(data.svg_handle_image); g_object_unref(data.svg_handle_svg); g_object_unref(data.pixbuf); g_object_unref(data.builder); } 

परिणाम है:


यदि चौड़ाई और ऊंचाई मान एसवीजी मापदंडों में सेट किए गए हैं, तो पीएनजी को निर्यात करते समय तस्वीर धुंधली हो सकती है।

आप प्रोग्राम को चौड़ाई और ऊंचाई भी बदल सकते हैं। इसके लिए मैंने अलग-अलग फाइलें बनाईं
svg_to_pixbuf_class.c और svg_to_pixbuf_class.h। यही है, फ़ाइल परिवर्तन चौड़ाई, ऊंचाई में खुलती है।

इसे / dev / shm / में सेव किया जाता है। Svg_handle को जानकारी निर्यात करने के बाद, आपको फ़ाइल और फ़ाइल के लिए लाइन पथ को हटाने की आवश्यकता है। आंशिक चौड़ाई / लंबाई मान भी समर्थित हैं।

svg_to_pixbuf_class.c
 #include <string.h> #include <stdlib.h> #include <stdio.h> #include <gtk/gtk.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <math.h> #include <stdbool.h> int char_to_digit(char num) { switch(num) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case '.': return -1; default: return -2; } } //     text double read_num_in_text(char* text) { double result=0; int i=0; bool fractional_flag=FALSE; char whole_part[16]={0}; char whole_digits=0; char fractional_part[16]={0}; char fractional_digits=0; while(char_to_digit(text[i])!=-2) { if(char_to_digit(text[i])!=-1&&!fractional_flag) { whole_part[whole_digits]=char_to_digit(text[i]); printf("text_num=%d|%c\n",char_to_digit(text[i]),text[i]); ++whole_digits; ++i; } else { if(char_to_digit(text[i])==-1) { printf("fractional flag is true\n"); fractional_flag=TRUE; ++i; } else { fractional_part[fractional_digits]=char_to_digit(text[i]); ++fractional_digits; printf("frac_digit=%d|%c\n",char_to_digit(text[i]),text[i]); ++i; } } } ///    i=whole_digits; result=whole_part[whole_digits]; while(i>0) { --i; printf("whole=%d\n",whole_part[i]); result=result+pow(10,whole_digits-i-1)*whole_part[i]; } i=0; while(i<=fractional_digits) { result=result+pow(0.1,i+1)*fractional_part[i]; ++i; } printf("result_read_num=%lf\n",result); return result; } //  ,    // int count_of_digits_for_delete(char* text) { int i=0; bool fractional_flag=FALSE; char whole_part[16]={0}; int whole_digits=0; char fractional_part[16]={0}; int fractional_digits=0; while(char_to_digit(text[i])!=-2) { if(char_to_digit(text[i])!=-1&&!fractional_flag) { whole_part[whole_digits]=char_to_digit(text[i]); printf("text_num=%d|%c\n",char_to_digit(text[i]),text[i]); ++whole_digits; ++i; } else { if(char_to_digit(text[i])==-1) { printf("fractional flag is true\n"); fractional_flag=TRUE; ++i; } else { fractional_part[fractional_digits]=char_to_digit(text[i]); ++fractional_digits; printf("frac_digit=%d|%c\n",char_to_digit(text[i]),text[i]); ++i; } } } if(fractional_flag) return whole_digits+1+fractional_digits; else return whole_digits; } //      /dev/shm //      char* create_dump_file(char *file_with_path) { char *file=NULL; int i=0; while(file_with_path[i]!='\0') {++i;} while(file_with_path[i]!='/'&&i>0) {--i;} file=file_with_path+i; GString *string=g_string_new("test -f /dev/shm"); g_string_append(string,file); g_string_append(string,"|| touch /dev/shm/"); g_string_append(string,file); system(string->str); ///  -  GString *full_path=g_string_new("/dev/shm"); g_string_append(full_path,file); char *result=g_string_free(full_path,FALSE); return result; } //result must be freed with g_string_free GString* read_file_in_buffer(char *file_with_path) { FILE *input = NULL; struct stat buf; int fh, result; char *body=NULL; // GString *resultat=g_string_new(""); fh=open(file_with_path, O_RDONLY); result=fstat(fh, &buf); if (result !=0) printf("  \n"); else { printf("%s",file_with_path); printf(" : %ld\n", buf.st_size); printf(" : %lu\n", buf.st_dev); printf(" : %s", ctime(&buf.st_atime)); input = fopen(file_with_path, "r"); if (input == NULL) { printf("Error opening file"); } body=(char*)calloc(buf.st_size+64,sizeof(char)); //    //    if(body==NULL) { printf("      body\n"); } int size_count=fread(body,sizeof(char),buf.st_size, input); if(size_count!=buf.st_size) printf("   "); resultat=g_string_append(resultat,body); free(body); } fclose(input); return resultat; } void* write_string_to_file(char* writed_file, char* str_for_write, int lenght) { FILE * ptrFile = fopen (writed_file ,"wb"); size_t writed_byte_count=fwrite(str_for_write,1,lenght,ptrFile); //if(writed_byte_count>4) return TRUE; //else return FALSE; fclose(ptrFile); } //      g_free char* get_resized_svg(char *file_with_path, int width, int height) { char *writed_file=create_dump_file(file_with_path); //       GString *body=read_file_in_buffer(file_with_path); char *start_search=NULL; char *end_search=NULL; char *width_start=NULL; char *width_end=NULL; char *height_start=NULL; char *height_end=NULL; start_search=strstr(body->str,"<svg"); int j=0; //   if(start_search) { end_search=strstr(start_search,">"); if(end_search) { ///  width width_start=strstr(start_search,"width"); width_end=width_start+strlen("width"); ///   width    while(width_end[j]==0x0A||width_end[j]==0x20) ++j; if(width_end[j]=='=') ++j; while(width_end[j]==0x0A||width_end[j]==0x20) ++j; if(width_end[j]!='"') printf("   svg.     width=%c\n",width_end[j]); else ++j; ///  ///  ,   gssize size=count_of_digits_for_delete(width_end+j); ///   (1  - 1 ) gssize pos=width_end+j-body->str; ///       g_string_erase(body,pos,size); char width_new[8]; g_snprintf(width_new,8,"%d",width); g_string_insert(body, pos, width_new); ///  height height_start=strstr(start_search,"height"); height_end=height_start+strlen("height"); ///   height    j=0; while(height_end[j]==0x0A||height_end[j]==0x20) ++j; if(height_end[j]=='=') ++j; while(height_end[j]==0x0A||height_end[j]==0x20) ++j; if(height_end[j]!='"') printf("   svg. \    height=%c%c%c\n",height_end[j-1],height_end[j],height_end[j+1]); else ++j; ///  ///  ,   size=count_of_digits_for_delete(height_end+j); ///   (1  - 1 ) pos=height_end+j-body->str; ///       g_string_erase(body,pos,size); char height_new[8]; g_snprintf(height_new,8,"%d",height); g_string_insert(body, pos, height_new); ///      dev/shm/ ///   write_string_to_file(writed_file,body->str,strlen(body->str)); return writed_file; //g_free(writed_file); g_string_free(body,TRUE); } else printf(" :      svg"); } } void resized_svg_free(char *path) { if (remove (path)==-1 ) { printf("    %s\n",path); } } 


svg_to_pixbuf_class.h
 #ifndef SVG_TO_PIXBUF_CLASS_H #define SVG_TO_PIXBUF_CLASS_H void resized_svg_free(char *path); char* get_resized_svg(char *file_with_path, int width, int height); //result must be freed with g_free() #endif 


अब बाईं ओर का आकार बदलें (जो GtkDrawingArea है)
 gboolean drawingarea1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { if(!data.svg_handle_svg) { char* path=get_resized_svg("/home/alex/svg_habr/compassmarkings.svg", 220, 220); data.svg_handle_svg=rsvg_handle_new_from_file(path,NULL); resized_svg_free(path); g_free(path); } gboolean result=rsvg_handle_render_cairo(data.svg_handle_svg,cr); if(result&&cr) {cairo_stroke(cr);} else printf(" \n"); return FALSE; } 

जैसा कि आप देख सकते हैं, यहां एक अप्रिय विशेषता है - पूर्ण पथ। यही है, यह फ़ोल्डर को स्थानांतरित करने के लायक है, क्योंकि बाएं भाग (जो कि GtkDrawingArea) प्रदर्शित होना बंद कर देता है। वही सभी संसाधनों पर लागू होता है जो निष्पादन योग्य फ़ाइल में शामिल नहीं हैं। ऐसा करने के लिए, मैंने एक फ़ंक्शन लिखा जो निष्पादन योग्य फ़ाइल के लिए पूर्ण पथ की गणना करता है, भले ही यह कैसे चलाया गया हो।

 //   data.path void get_real_path(char *argv0) { char* result=(char*)calloc(1024,sizeof(char)); char* cwd=(char*)calloc(1024,sizeof(char)); getcwd(cwd, 1024); int i=0; while(argv0[i]!='\0'&&i<1024) ++i; while(argv0[i]!='/'&&i>0) --i; result[i]='\0'; while(i>0) { --i; result[i]=argv0[i]; } /*alex@alex-System-Product-Name:~/project_manager$ ./manager.elf argv[0]=./manager.elf path=/home/alex/project_manager*/ if(strlen(result)<=strlen(cwd)) //   { free(result); strcpy(data.path,cwd); strcat(data.path,"/"); //printf("path_cwd=%s\n",cwd); free(cwd);} else { /*alex@alex-System-Product-Name:/home$ '/home/alex/project_manager/manager.elf' argv[0]=/home/alex/project_manager/manager.elf path=/home*/ free(cwd); strcpy(data.path,result); strcat(data.path,"/"); //printf("path_result=%s\n",result); free(result); } } 

कोड में 2 उदाहरण हैं कि कैसे मैनेजर को चलाना है। फाइल। आपको फ़ंक्शन की शुरुआत में मुख्य () डालने की भी आवश्यकता है

 char cwd[1024]; getcwd(cwd, sizeof(cwd)); get_real_path(argv[0]); 

ड्राइंग फ़ंक्शन निम्नलिखित रूप लेगा

 gboolean drawingarea1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { if(!data.svg_handle_svg) { char image_path[1024]; strcat(image_path,data.path); strcat(image_path,"compassmarkings.svg"); printf("image_path=%s\n",image_path); char* path=get_resized_svg(image_path, 220, 220); data.svg_handle_svg=rsvg_handle_new_from_file(path,NULL); resized_svg_free(path); g_free(path); } gboolean result=rsvg_handle_render_cairo(data.svg_handle_svg,cr); if(result&&cr) {cairo_stroke(cr);} else printf(" \n"); return FALSE; } 

त्वरित कार्रवाई परीक्षण।

हमारे पास 2 ड्रा फ़ंक्शन (GtkDrawingArea और GtkImage) हैं।

हम उनमें से प्रत्येक को एक प्रकार के डिजाइन में जोड़ेंगे (बिना कनेक्ट किए भूल जाएं <time.h>)

 clock_t tic = clock(); clock_t toc = clock(); printf("image1_draw_cb elapsed : %f seconds\n", (double)(toc - tic) / CLOCKS_PER_SEC); 

और htop आवेदन में, आप देख सकते हैं कि कार्यक्रम प्रत्येक कोर एथलोन 2 X3 2.5 गीगाहर्ट्ज का 20-30% कैसे खाता है।

त्रुटि जल्दी मिल गई थी।

 gboolean image1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { clock_t tic = clock(); if(!data.svg_handle_image) { data.svg_handle_image=rsvg_handle_new_from_file("compassmarkings.svg",NULL); data.surf=cairo_image_surface_create_from_png("2.png"); data.pixbuf=rsvg_handle_get_pixbuf(data.svg_handle_image); //} //if(data.pixbuf) // { cairo_set_source_surface(cr,data.surf,0,0); GdkPixbuf *dest=gdk_pixbuf_scale_simple (data.pixbuf,250,250,GDK_INTERP_BILINEAR); gtk_image_set_from_pixbuf (data.image,dest); g_object_unref(dest); //cairo_paint(cr); } clock_t toc = clock(); printf("image1_draw_cb elapsed : %f seconds\n", (double)(toc - tic) / CLOCKS_PER_SEC); return FALSE; } 

जैसा कि यह निकला, GtkImage का अपना रेंडरिंग सिस्टम है, और image1_draw_cb की सामग्री को केवल एक बार आरंभीकृत किया जा सकता है। टिप्पणी की गई पंक्तियाँ बेमानी थीं।



जैसा कि आप देख सकते हैं, पहली बार प्रतिपादन GtkDmingArea की तुलना में GtkImage के साथ अधिक समय लेता है, लेकिन सैद्धांतिक रूप से छवि को अपडेट करना तेज होना चाहिए। प्रत्येक 220px * 220px छवि रिड्रास्टिंग के लिए 4 मिलियन प्रोसेसर चक्र थोड़ा अधिक है, और आप केवल पिक्सब्यूफ के माध्यम से कैश कर सकते हैं (कम से कम, मुझे अन्य तरीकों का पता नहीं है)।

आपका ध्यान के लिए धन्यवाद।

Source: https://habr.com/ru/post/hi435564/


All Articles