Hi T_T, hôm nay chúng ta sẽ qua 1 bài khác là in ra cửa sổ có chữ Hello Word và các bạn sẽ thấy nó dễ như kẹo vậy.
Chúng ta hãy bắt đầu với 1 số khái niệm cơ bản và hàm trong gtkmm :
I. Signal
- gtkmm nó cũng như mọi GUI toolkit khác, cũng là event-driven. Nghĩa là khi 1 sự kiện được xảy ra như ta click chuột, hay ấn phím thì tín hiệu sẽ được phát ra bởi 1 Widget. Và mỗi Widget sẽ có nhiều signal khác nhau. Và để tạo ra 1 hiệu quả ấn nút ta sẽ dùng 1 signal-handler để catch các tín hiệu này.
Và gtkmm nó sử dụng thư viện libsigc++ để thực hiện các tín hiệu. Ví dụ chúng ta có 1 nút tín hiệu click được send vào bởi hàm ( singnal handler ) gọi là click_button_please.
Ở đây :C++ Code:
m_button.signal_clicked().connect( sigc::mem_fun(*this, &HelloWord::click_button_please) );
- HelloWord là 1 class chúng ta định nghĩa.
- click_button_please() là hàm thành viên của nó dùng để xử lý tính hiệu.
- m_button là 1 nút của lớp HelloWord, nó có dạng Gtk::Button m_button.
- connect chỉ là syntax mặc định của gtkmm dùng để nhận tín hiệu mà thôi.
- hàm signal_clicked() cũng vậy, là của gtkmm nên chúng ta không cần lo lắng làm gì lúc này.
- còn mem_fun là 1 dạng function object quen thuộc của STL. Nếu chúng ta chưa biết về nó, ok, take a quick tour :
..
Xem ví dụ sau :
Quá quen thuộc phải không T_T, giả sử print() là 1 hàm thành viên như thế, và ta có 1 con trỏ Element* dùng để gọi các đối tượng e1, e2, e3...lúc run time. Khi gọi print() ta dùng E[j]->print(). Nhưng khi dùng container thì truy xuất trực tiếp như thế giảm performance về rất nhiều mặc -> lỗi out of bound. Vì vậy trong các container của STL nó mới nảy sinh ra các iterator. Bây h chúng ta có thể viết gọn là 1 dòng dùng STL xem sao :C++ Code:
#include <iostream> #include <vector> using namespace std; class Element { public: }; int main() { Element e1,e2,e3; vector<Element*> E; E.push_back(&e1); E.push_back(&e2); E.push_back(&e3); for (int j=0;j<E.size();j++) E[j]->print(); }
Nhìn, OMG, code đọc vào muốn ngất ngư, sao mà phức tạp thế T_T. Đơn giản hơn tí nữa xem nàoC++ Code:
vector<Element*>::iterator; for (vector<Element*>::iterator i=E.begin();i<E.end();i++) (*i)->print();
for_each nhận 2 con trỏ đầu và đuôi + 1 function object print. Nhưng khoang, for_each nhận 1 global fucntion chứ không được là hàm thành viên :(. Vậy compiler nó sẽ complain. Và chúng ta sẽ nghĩ đến 1 giải pháp là :C++ Code:
for_each (E.begin(),E.end(), &Element::print);
Nhưng code này thật sự là quá không mấy hiệu quả, khi giả sử chúng ta có nhiều global function như vậy. Vậy thì mem_fun của STL sẽ là giải pháp tuyệt vời :C++ Code:
void do_print(Element *e) { e->print(); } ... for_each (E.begin(),E.end(), do_print);
1 dòng là, xong T_T, mem_fun nhận 1 con trỏ tới hàm thành viên, nhìn cú pháp thì quái chiêu thế nhưng đặt mắt vào nhìn chừng 2 phút là ok ngayC++ Code:
for_each (E.begin(),E.end(), mem_fun(&Element::print));.
Một ví dụ nữa cho quen luôn nhé :
Vậy là chúng ta đã hiểu hầu hết các chức năng trong 1 hàm rất thường dùng trong gtkmm. Let's move on T_T.C++ Code:
#include <iostream> #include <vector> #include <functional> using namespace std; class Shape { public: }; class Triangle : public Shape { }; class Square : public Shape { }; int main() { vector<Shape*> shapes; Triangle t1,t2; Square s1, s2; shapes.push_back(&t1); shapes.push_back(&s1); shapes.push_back(&t2); shapes.push_back(&s2); for_each( shapes.begin(), shapes.end(), mem_fun(&Shape::draw)); }
II. HelloWord program :
Và quả thật khi học gtkmm bạn sẽ có cảm giác bạn đang thực sự học C++, nó không như những toolkit khác, nó không sinh code làm cho bạn không thể nào hiểu nổi nó từ đâu ra mà chỉ ngậm ngùi chấm nhận. Học gtkmm bạn càng cảm thấy thích C++ hơn bao h hết.
Xem ví dụ sau nào :
- Chúng ta có 1 class print_hello do ta chúng ta định nghĩa, và lớp này sẽ kế thừa từ lớp Gtk::Window, và nó có 1 thành viên là Gtk::Button m_button.C++ Code:
#include <gtkmm/button.h> #include <gtkmm/window.h> #include <gtkmm/main.h> #include <iostream> class print_hello : public Gtk::Window{ public : print_hello(); virtual ~print_hello(); protected : virtual void clicked_button(); Gtk::Button m_button; }; //constructor //"pressed" ở đây là label của nút m_button print_hello::print_hello():m_button("pressed"){ set_border_width(100); //-------- method Gtk::Window m_button.signal_clicked().connect(sigc::mem_fun(*this, &print_hello::clicked_button)); add(m_button); //--------- method of class Gtk::Window m_button.show(); //----------- method of Gtk::button } //destructor print_hello::~print_hello(){ } void print_hello::clicked_button(){ } int main(int argc, char* argv[]){ Gtk::Main my_kit(argc, argv); print_hello hello; Gtk::Main::run(hello); return 0; }
Tất cả các hàm nào của Gtk:: mình đã chú thích rõ ràng trên đó. Nhìn thoáng quá sao mà nhiều quá, nhìn kĩ lại sao mà ít quá vậy T_T, bắt đầu kết gtkmm rùi nhá T_T.
Bây h hãy nhìn vào main() còn đơn giản hơn :
- my_kit là 1 đối tượng của Gtk::Main đòi hỏi cho mọi chuơng trình gtkmm và nó nhận 2 đối mặc định argc và argv. Cái này chỉ là sugar syntax thôi, có 1 dòng cứ học thuộc là được. my_kit là tên do chúng ta đặt, muốn đặt gì đó là tùy.C++ Code:
int main(int argc, char* argv[]){ Gtk::Main my_kit(argc, argv); print_hello hello; Gtk::Main::run(hello); return 0;
- Sau đó ta tạo 1 đối tượng hello và pass nó vào hàm run của Gtk::Main và thế là chúng ta sẽ có cửa sổ nhỏ xinh thế này đây T_T :
Và khi click vào cái nút press ấy thì tau thấy có 1 dòng chữ Welcome to GTKMM hiện ra, quá cool phải không nào!
Khi dịch xong thì ta có thể chạy chượng trình bằng cú pháp, ví dụ ta có file executable :
Thì ta run nó bằng :Code:g++ hello.cpp -o hello -Wall -O3 -ansi -pedantic `pkg-config gtkmm-2.4 --cflags --libs`
- Nếu các bạn có mọi khó khăn nào trong cách compile source code on Linux, thì nói với mình nhé, mình sẽ chỉ dẫn tối đaCode:./hello.
Have fun...!