บทความที่ผ่านมาเราได้เริ่มต้นเขียนโปรแกรมด้วย Gtk4 ไปกันแล้ว หลักการเบื้องต้นก็สร้าง GtkApplication ในฟังค์ชัน main() แล้วเชื่อมต่อ signal ชื่อ activate ไปยังฟังค์ชันชื่อ app_activate เพื่อสร้างวินโดว์สำหรับเริ่มต้น ทุกอย่างจะเริ่มต้นที่ฟังค์ชัน app_activate (หรือชื่ออื่น) ที่เราเชื่อมโยงกับ signal ชื่อ active
ตอนที่ 2 นี้ก็จะเป็นการเพิ่ม GtkLabel, GtkButton และ GtkBox ให้กับวินโดว์ที่สร้างขึ้น รายละเอียดก็เริ่มกันดังนี้
ตัวอย่างโปรแกรม
1 #include <gtk/gtk.h> 2 3 static void 4 app_activate (GApplication *app, gpointer user_data) { 5 GtkWidget *win; 6 GtkWidget *lbl; //add 7 8 win = gtk_application_window_new (GTK_APPLICATION (app)); 9 gtk_window_set_title (GTK_WINDOW (win), "GtkLabel"); 10 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); 11 12 lab = gtk_label_new ("CFromZero"); //add 13 gtk_window_set_child (GTK_WINDOW (win), lbl); //add 14 15 gtk_widget_show (win); 16 } 17 18 int 19 main (int argc, char **argv) { 20 GtkApplication *app; 21 int stat; 22 23 app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_FLAGS_NONE); 24 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 25 stat =g_application_run (G_APPLICATION (app), argc, argv); 26 g_object_unref (app); 27 return stat; 28 }
ภาพหน้าจอแสดงข้อความด้วย GtkLabel |
ฟังค์ชัน gtk_window_set_chid(GTK_WINDOW(win), lbl) เป็นการสั่งให้เพิ่ม GtkWidget *lbl เข้าไปใน GtkWidget *win ตรงนี้เรียกว่า child widget ซึ่งจะแตกต่างจาก child object โดย object กับ widget ต่างก็มี child ที่จะสืบทอด เพียงแต่มีความแตกต่างกัน (จะหารายละเอียดมาเพิ่มเติมภายหลัง)
ในที่นี้ GtkWindow ที่อ้างอิงกับ win จะไม่มี Parents จึงถูกเรียกว่า top-level window และใน GtkApplication จะมีหลาย top-level window ได้ (จะหารายละเอียดเพิ่มเติมอีกที)
GtkButton
ลำดับต่อไปจะกล่าวถึง GtkButton ซึ่งเป็นปุ่มกด ที่แสดงไว้ในหน้าจอวินโดว์ โดยปุ่มกดนั้นก็จะมีข้อความกำกับด้วย เมื่อคลิกปุ่มนั้น ก็จะเชื่อมไปยังฟังค์ชันใดๆ ตามที่กำหนด โดยผ่าน signal ชื่อ clicked
ตัวอย่างโปรแกรม
1 #include <gtk/gtk.h> 2 3 static void 4 btn_clicked (GtkButton *btn, gpointer user_data) { 5 g_print("%s, Clicked.\n", (gchar*) user_data); 6 } 7 8 static void 9 app_activate (GApplication *app, gpointer user_data) { 10 GtkWidget *win; 11 GtkWidget *btn; 12 13 win = gtk_application_window_new (GTK_APPLICATION (app)); 14 gtk_window_set_title (GTK_WINDOW (win), "lb2"); 15 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); 16 17 btn = gtk_button_new_with_label ("Click me"); 18 gtk_window_set_child (GTK_WINDOW (win), btn); 19 g_signal_connect (btn, "clicked", G_CALLBACK (btn_clicked), (gpointer) "Button"); 20 21 gtk_widget_show (win); 22 } 23 24 int 25 main (int argc, char **argv) { 26 GtkApplication *app; 27 int stat; 28 29 app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_FLAGS_NONE); 30 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 31 stat =g_application_run (G_APPLICATION (app), argc, argv); 32 g_object_unref (app); 33 return stat; 34 }
จากตัวอย่างด้านบนได้เพิ่มในส่วนโปรแกรมดังนี้
กำหนดปุ่มให้มีชื่อเป็น btn เป็น GtkWidget แล้วสร้างปุ่มด้วยคำสั่ง
17 btn = gtk_button_new_with_label ("Click me");
18 gtk_window_set_child (GTK_WINDOW (win), btn);
19 g_signal_connect (btn, "clicked", G_CALLBACK (btn_clicked), (gpointer) "Button");
บรรทัดที่ 19 กำหนด signal ให้กับปุ่มกดอ้างอิงถึง btn โดยกำหนดอีเวนต์ clicked เมื่อมีการคลิกปุ่มให้เรียกใช้ฟังค์ชัน btn_clicked และตัวอย่างนี้ผมส่งข้อความ Button ไปด้วยเมื่อมีการคลิกปุ่มกด
สำหรับฟังค์ชัน btn_clicked ก็มีดังนี้
3 static void 4 btn_clicked (GtkButton *btn, gpointer user_data) { 5 g_print("%s, Clicked.\n", (gchar*) user_data); 6 }
บรรทัดที่ 4 btn_clicked เป็นชื่อฟังค์ชัน มีพารามิเตอร์ 2 ตัว โดยตัวแรก GtkButton *btn ก็คือ ตัวปุ่มกดนั่นแหละ เป็นค่าโดยปริยาย เราไม่ต้องส่งอาร์กิวเมนต์มา (ลองสังเกตบรรทัดที่ 19 เราส่งมาเพียงแต่ข้อความ (gpointer) "Button" อันเดียวเท่านั้น ซึ่งเป็นอาร์กิวเมนต์ตัวที่ 2 ในบรรทัดที่ 4 คือ gpointer user_data โดยสิ่งที่ส่งมานี้เป็น pointer ชี้ไปยังที่เก็บข้อความ "Button" และเวลาจะใช้งาน หรือแสดงข้อความ "Button" ออกทางหน้าจอก็ cast หรือแปลงให้เป็นข้อความหรือ gchar* เสียก่อน ตามบรรทัดที่ 5
5 g_print("%s, Clicked.\n", (gchar*) user_data);
ระบบก็จะวิ่งไปตำแหน่งที่ส่งมาแล้วอ่านเอาข้อมูลที่เป็นข้อความแล้วนำมาแสดงผลในคำสั่ง g_print() ดังนั้นเมื่อมีการคลิกปุ่มกดก็จะมีข้อความ Button, Clicked. ออกทางหน้าจอทุกครั้ง
ตัวอย่างต่อไปยังอยู่ที่ GtkButton เหมือนเดิม แต่รอบนี้จะส่งอินสแตนซ์ win ไปด้วย ดังนี้
1 static void 2 btn_clicked (GtkButton *btn, gpointer user_data) { 3 GtkWindow *win = GTK_WINDOW (user_data); 4 gtk_window_destroy (win); 5 } 6 7 static void 8 app_activate (GApplication *app, gpointer user_data) { 9 GtkWidget *win; 10 GtkWidget *btn; 11 12 win = gtk_application_window_new (GTK_APPLICATION (app)); 13 gtk_window_set_title (GTK_WINDOW (win), "lb3"); 14 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); 15 16 btn = gtk_button_new_with_label ("Quit"); 17 gtk_window_set_child (GTK_WINDOW (win), btn); 18 g_signal_connect (btn, "clicked", G_CALLBACK (btn_clicked), win); 19 20 gtk_widget_show (win); 21 } 22 23 int 24 main (int argc, char **argv) { 25 GtkApplication *app; 26 int stat; 27 28 app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_FLAGS_NONE); 29 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 30 stat =g_application_run (G_APPLICATION (app), argc, argv); 31 g_object_unref (app); 32 return stat; 33 }
18 g_signal_connect (btn, "clicked", G_CALLBACK (btn_clicked), win);
3 GtkWindow *win = GTK_WINDOW (user_data);
ถ้าคลิกปุ่ม Quit โปรแกรมก็จะถูกปิดตามคำสั่งในฟังค์ชัน btn_clicked |
GtkBox
GtkWindow และ GtkApplicationWindow สามารถมีได้เพียง child เดียวเท่านั้น นั่นหมายความว่าเราจะเพิ่มปุ่มไปแล้วจะเพิ่ม child ที่เป็นข้อความเพิ่มอีกไม่ได้
ถ้าเราพยายามเพิ่ม child ใหม่ลงไป child เก่าก็จะไม่ถูกนำมาแสดง คือ จะแสดงเฉพาะอันใหม่เท่านั้น
ดังนั้นหากต้องการเพิ่มหลายๆ widget ลงไปก็ต้องเพิ่มเข้าไปในกล่องอะไรสักอย่างหนึ่ง แล้วค่อยเอากล่องนั้นไปเป็น child ให้กับ window ใน Gtk เรียกว่า GtkBox โดยมีหลักการดังนี้
- สร้าง GtkApplicationWindow
- สร้าง GtkBox เป็น child แล้วเพิ่มลงไปใน GtkApplicationWindow
- สร้าง GtkButton แล้วเพิ่มเข้าไปใน GtkBox
- สร้าง GtkButton หรือ widget อื่นๆ แล้วเพิ่มลงในกล่อง GtkBox ได้อีก
โดยระบบจะมีผังดังนี้
ตัวอย่างโปรแกรม
#include <gtk/gtk.h> static void btn_clicked1 (GtkButton *btn, gpointer user_data){ g_print("Button 1 clicked.\n"); } static void btn_clicked2 (GtkButton *btn, gpointer user_data){ GtkWindow *win = GTK_WINDOW(user_data); gtk_window_destroy(win); } static void app_activate (GApplication *app, gpointer user_data){ GtkWidget *win; GtkWidget *box; GtkWidget *lbl; GtkWidget *btn1; GtkWidget *btn2; win = gtk_application_window_new(GTK_APPLICATION(app)); gtk_window_set_title(GTK_WINDOW(win), "GtkBox"); gtk_window_set_default_size(GTK_WINDOW(win), 400,200); box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); gtk_box_set_homogeneous(GTK_BOX(box), TRUE); gtk_window_set_child(GTK_WINDOW(win), box); lbl = gtk_label_new("CFromZero"); btn1 = gtk_button_new_with_label("Button 1"); g_signal_connect(btn1, "clicked", G_CALLBACK(btn_clicked1), NULL); btn2 = gtk_button_new_with_label("Button 2 (Quit)"); g_signal_connect(btn2, "clicked", G_CALLBACK(btn_clicked2), win); gtk_box_append(GTK_BOX(box), lbl); gtk_box_append(GTK_BOX(box), btn1); gtk_box_append(GTK_BOX(box), btn2); gtk_widget_show(win); } int main(int argc, char **argv){ GtkApplication *app; int stat; app = gtk_application_new("com.example.www", G_APPLICATION_FLAGS_NONE); g_signal_connect(app, "activate", G_CALLBACK(app_activate), NULL); stat = g_application_run(G_APPLICATION(app), argc, argv); g_object_ref(app); return stat; }
ตัวอย่างหน้าจอขณะรันโปรแกรม
หน้าจอตัวอย่างโปรแกรมเมื่อคลิกปุ่ม Button 1 ก็จะแสดงข้อความที่หน้าจอ |
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_box_set_homogeneous(GTK_BOX(box), TRUE);
gtk_window_set_child(GTK_WINDOW(win), box);
จากคำสั่งสามบรรทัดข้างบนนี้ บรรทัดแรก box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); คือ สร้างกล่องขึ้นมา 1 อันให้จัดเรียง widget ที่เพิ่มเข้ามาให้อยู่แนวตั้ง และกำหนดระยะห่างระหว่างแต่ละ widget อยู่ 5 pixel
ถัดมา gtk_box_set_homogeneous(GTK_BOX(box), TRUE); กำหนดให้ ทุก widget ที่เพิ่มเข้ามาใช้พื้นที่ความยาวเต็มหน้าเท่ากันทุก widget
บรรทัดที่ 3 gtk_window_set_child(GTK_WINDOW(win), box); เป็นการเพิ่ม box เข้าไปใน win หลังจากนั้นก็เพิ่ม widget อื่นๆ ด้วยคำสั่ง gtk_box_append(GTK_BOX(box), lbl); เข้าไปตามลำดับบนลงล่าง
ทั้งหมดที่กล่าวมานี้ คือ การสร้าง GtkWidget เพื่อสร้าง Label หรือป้ายชื่อ โดยใช้ GtkLabel, สร้างปุ่มกดโดยใช้ GtkButton และสร้างกล่องสำหรับบรรจุ widget ต่างๆ โดยใช้ GtkBox
สิ่งเหล่านี้ก็เป็นพื้นฐานในการแสดงผลของโปรแกรมที่เราเขียนขึ้นหากค่อยๆ ทำความเข้าใจต่อไปก็เรียนรู้เพิ่มเติมได้ไม่ยากนัก...
เรียบเรียงจาก
https://github.com/ToshioCP/Gtk4-tutorial/blob/main/gfm/sec4.md
ไม่มีความคิดเห็น:
แสดงความคิดเห็น