Thứ Sáu, 13 tháng 9, 2013

CPU Branch Predictor and How make if-else (branching) faster

Consider a railroad junction:
Image by Mecanismo
Now for the sake of argument, suppose this is back in the 1800s - before long distance or radio communication.
You are the operator of a junction and you hear a train coming. You have no idea which way it will go. You stop the train to ask the captain which direction he wants. And then you set the switch appropriately.
Trains are heavy and have a lot of momentum. So they take forever to start up and slow down.
Is there a better way? You guess which direction the train will go!
  • If you guessed right, it continues on.
  • If you guessed wrong, the captain will stop, back up, and yell at you to flip the switch. Then it can restart down the other path.
If you guess right every time, the train will never have to stop.
If you guess wrong too often, the train will spend a lot of time stopping, backing up, and restarting.

Consider an if-statement: At the processor level, it is a branch instruction:
enter image description here
You are a processor and you see a branch. You have no idea which way it will go. What do you do? You halt execution and wait until the previous instructions are complete. Then you continue down the correct path.
Modern processors are complicated and have long pipelines. So they take forever to "warm up" and "slow down".
Is there a better way? You guess which direction the branch will go!
  • If you guessed right, you continue executing.
  • If you guessed wrong, you need to flush the pipeline and roll back to the branch. Then you can restart down the other path.
If you guess right every time, the execution will never have to stop.
If you guess wrong too often, you spend a lot of time stalling, rolling back, and restarting.

This is branch prediction. I admit it's not the best analogy since the train could just signal the direction with a flag. But in computers, the processor doesn't know which direction a branch will go until the last moment.
So how would you strategically guess to minimize the number of times that the train must back up and go down the other path? You look at the past history! If the train goes left 99% of the time, then you guess left. If it alternates, then you alternate your guesses. If it goes one way every 3 times, you guess the same...
In other words, you try to identify a pattern and follow it. This is more or less how branch predictors work.
Most applications have well-behaved branches. So modern branch predictors will typically achieve >90% hit rates. But when faced with unpredictable branches with no recognizable patterns, branch predictors are virtually useless.

As hinted from above, the culprit is this if-statement:

if (data[c] >= 128)
    sum += data[c];
Notice that the data is evenly distributed between 0 and 255. When the data is sorted, roughly the first half of the iterations will not enter the if-statement. After that, they will all enter the if-statement.
This is very friendly to the branch predictor since the branch consecutively goes the same direction many times. Even a simple saturating counter will correctly predict the branch except for the few iterations after it switches direction.
Quick visualization:
 T = branch taken  
 N = branch not taken  
 data[] = 0, 1, 2, 3, 4, ... 126, 127, 128, 129, 130, ... 250, 251, 252, ...  
 branch = N N N N N ...  N  N  T  T  T ...  T  T  T ...  
     = NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT (easy to predict)  

However, when the data is completely random, the branch predictor is rendered useless because it can't predict random data. Thus there will probably be around 50% misprediction. (no better than random guessing)





 data[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118, 14, 150, 177, 182, 133, ...  
 branch =  T,  T,  N,  T,  T,  T,  T, N,  T,  N,  N,  T,  T,  T,  N ...  
     = TTNTTTTNTNNTTTN ...  (completely random - hard to predict)  

So what can be done?
If the compiler isn't able to optimize the branch into a conditional move, you can try some hacks if you are willing to sacrifice readability for performance.
Replace:
 if (data[c] >= 128)  
   sum += data[c];  

with:


 int t = (data[c] - 128) >> 31;  
 sum += ~t & data[c];  

This eliminates the branch and replaces it with some bitwise operations.
(Note that this hack is not strictly equivalent to the original if-statement. But in this case, it's valid for all the input values of data[].)
Benchmarks: Core i7 920 @ 3.5 GHz
C++ - Visual Studio 2010 - x64 Release

 // Branch - Random  
 seconds = 11.777  
 // Branch - Sorted  
 seconds = 2.352  
 // Branchless - Random  
 seconds = 2.564  
 // Branchless - Sorted  
 seconds = 2.587  

Java - Netbeans 7.1.1 JDK 7 - x64


 // Branch - Random  
 seconds = 10.93293813  
 // Branch - Sorted  
 seconds = 5.643797077  
 // Branchless - Random  
 seconds = 3.113581453  
 // Branchless - Sorted  
 seconds = 3.186068823  

Observations:
  • With the Branch: There is a huge difference between the sorted and unsorted data.
  • With the Hack: There is no difference between sorted and unsorted data.
  • In the C++ case, the hack is actually a tad slower than with the branch when the data is sorted.
A general rule of thumb is to avoid data-dependent branching in critical loops. (such as in this example)

Update :
  • GCC 4.6.1 with -O3 or -ftree-vectorize on x64 is able to generate a conditional move. So there is no difference between the sorted and unsorted data - both are fast.
  • VC++ 2010 is unable to generate conditional moves for this branch even under /Ox.
  • Intel Compiler 11 does something miraculous. It interchanges the two loops, thereby hoisting the unpredictable branch to the outer loop. So not only is it immune the mispredictions, it is also twice as fast as whatever VC++ and GCC can generate! In other words, ICC took advantage of the test-loop to defeat the benchmark...
  • If you give the Intel Compiler the branchless code, it just out-right vectorizes it... and is just as fast as with the branch (with the loop interchange).
This goes to show that even mature modern compilers can vary wildly in their ability to optimize code...

Thứ Tư, 26 tháng 9, 2012

Interpreter pattern

Interpreter Pattern : Người phiên dịch



Interpreter Pattern giúp người lập trình có thể "xây dựng" những đối tượng "động" bằng cách đọc mô tả về đối tượng (file XML hoặc gì đó) rồi sau đó "xây dựng" đối tượng đúng theo mô tả đó.

Metadata (ngôn ngữ mô tả) --> [Interpreter Pattern] --> Đối tượng tương ứng.


Mẫu gồm 2 thành phần chính có liên hệ với nhau là Context Expression
  • Context chính là phần chứa thông tin biểu diễn mẫu chúng ta cần xây dựng
  • Expression là phần chứa các phép logic để đưa thông tin của context thành đối tượng cụ thể


Ví dụ 1: Biểu mẫu của ngày thay đổi dựa trên từng loại thể hiện (Ví dụ dd/mm/yy hay dd/mm/yy - hh:mm:ss). Giải quyết vấn đề làm sao xuất ra biểu mẫu đúng với từng loại thể hiện

Phương pháp thông thường : Dùng các câu lệnh rẽ nhánh if .... then ... else

 if(IsWordDate)  
 {  
      formattedDate = date.DayOfYear + "th day, " +  
      date.DayOfWeek + ", " +  
      ConvertMonth(date.Month) + " " +  
      ConvertDay(date.Day) + ", " +  
      date.Year;  
 }  
 else if (IsCalendarDate)  
 {  
      formattedDate = date.Month + "-" +  
      date.Day + "-" +  
      date.Year;  
 }  
 else if (IsGregorianDate)  
 {  
      formattedDate = date.Month + "-" +  
      date.Day + "-" + date.Year + " " +  
      date.Hour + ":" + date.Minute + ":" +  
      date.Millisecond;  
 }  
   

Giải pháp : Sử dụng mẫu Interpreter và các Expression Engine để quản lý sự khác nhau giữa các mẫu ngày bằng Expression Type.

Cài đặt theo mô hình:

   
 // Context  
 Class Context  
 {  
      private string _formattedDate;  
      private DateTime _date;  
        
      public Context(DateTime dateToFormat)  
      {  
           _date = dateToFormat;  
      }  
        
      public DateTime Date  
      {  
           get{return _date;}  
      }  
        
      public string FormattedDate  
      {  
           get{return _formattedDate;}  
           set{_formattedDate = value;}  
      }  
 }  
   


 //Interpreters  
 abstract class AbstractDateExpression  
 {  
      public abstract void Interpret(Context context);  
 }  


   
 class WordDateExpression : AbstractDateExpression  
 {  
      public override void Interpret(Context context)  
      {  
           context.FormattedDate = date.DayOfYear + "th day, " +  
           date.DayOfWeek + ", " +  
           ConvertMonth(date.Month) + " " +  
           ConvertDay(date.Day) + ", " +  
           date.Year;  
      }  
 }  


   
 class CalendarDateExpression : AbstractDateExpression  
 {  
      public override void Interpret(Context context)  
      {  
           context.FormattedDate = date.Month + "-" +  
                date.Day + "-" + date.Year;  
      }  
 }  
   


   
   
 class GregorianDateExpression : AbstractDateExpression  
 {  
      public override void Interpret(Context context)  
      {  
           context.FormattedDate = date.Month + "-" +  
                date.Day + "-" + date.Year + " " +  
                date.Hour + ":" + date.Minute + ":" +  
                date.Millisecond;  
      }  
 }  

Kết quả và test thử

 AbstractDateExpression exp = new WordDateExpression();  
 exp.Interpret(context);  
   
 // ---Result   
 // Format for WordDateExpression: 202th day, Friday, July  
   
 exp = new CalendarDateExpression();  
 exp.Interpret(context);  
   
 // ---Result  
 // Formate for ClaendarDateExpression : 7-21-2006  
   
   
 exp = new GregorianDateExpression();  
 exp.Interpret(context);  
   
 // ---Result  
 // Format for GregorianDateExpression: 7-21-2006 15:47:95  

Vậy là thông qua Interpreters chúng ta đã có thể đưa ra kết quả mong muốn ứng với từng context...
Sử dụng mẫu interpreters còn có thể giúp chúng ta tạo lập các đối tượng thông qua file XML chứa metadata. Ví dụ thường gặp trong game đó là việc load các máp động, giao diện game, thay đổi hình nền game, ... thông qua các file đặc tả nằm độc lập (vì nếu tích hợp mỗi lần cập nhật map mới sẽ đẩy xuống client 1 lượng dữ liệu khổng lồ).

Thứ Hai, 24 tháng 9, 2012

Command Pattern

Tiếp tục với các mẫu Behavioral Pattern, Ở phần trước ta đã đi sơ qua về mẫu Chain of Responsibility và lần này chúng ta đến với một mẫu cũng tương tự... đó là Command Pattern


Command Pattern

Command Pattern là mẫu cho phép tất cả những Request đến object được lưu trữ trong chính object đó dưới dạng 1 object command. Có nghĩa là nếu bạn chuyển 1 Request (đóng gói dưới dạng 1 command object) tới 1 chức năng nào đó của object thì command object của Request đó sẽ được giữ lại bên trong object chính.

Khái niệm Command Object giống như một class trung gian được tạo ra chuyên để lưu trữ các câu lệnhtrạng thái của object tại một thời điểm nào đó. Hay có thể hiểu nôm na là đóng gói Request thành 1 class.

Tại sao ta cần đến việc lưu trữ những Object Command (hay lưu log các Request)???
Chắc hẳn bạn đã từng dùng NotePad hay bất cứ 1 text editor nào như winword ... Mỗi khi bạn muốn Undo hay Redo một hành động ... Và văn bản chuyển về các trạng thái phía trước hay sau đó ... Đó là ứng dụng của Command Object.

Command Object gồm có 3 phần chính là:
  • Invoker : Đây sẽ là nơi lưu trữ và phát sinh Request (dưới dạng Command Object).
  • Command : Là đối tượng lưu giữ RequestTrạng thái của đối tượng tại 1 thời điểm
  • Receiver : Là đối tượng thực hiện lệnh trên mỗi Request.
Sau đây là sơ đồ UML của mẫu Command  Pattern


Vấn đề: Đối tượng văn bản cần 1 cách nào đó để thêm hoặc lưu trữ những hành động undo hay redo
 
Document Object có thiết lập như sau
 //receiver  
 class Document  
 {  
   private ArrayList _textArray = new ArrayList();  
   public void Write(string text)  
   {  
      _textArray.Add(text);  
   }  
   public void Erase(string text)  
   {  
      _textArray.Remove(text);  
   }  
   public void Erase(int textLevel)  
   {  
      _textArray.RemoveAt(textLevel);  
   }  
   public string ReadDocument()  
   {  
      System.Text.StringBuilder sb = new System.Text.StringBuilder();  
      foreach(string text in _textArray)  
        sb.Append(text);  
      return sb.ToString();  
   }  
 }  

Giải pháp: Sử dụng 1 đối tượng Command như 1 Request để lưu trữ các Text và cho phép command xử lý yêu cầu Undo và Redo.

Ta xây dựng 1 chương trình dựa trên UML sau :


  • Trong đó đối tượng DocumentInstance sẽ là đối tượng người dùng thao tác trực tiếp và cũng là Invoker.
  •  Trong đối tượng DocumentInstance sẽ có 1 Array các CommandObject (với thể hiện là DocumentEditCommand) để lưu trữ trạng thái của văn bản.
  • Đối tượng DocumentEditCommand là thể hiện trực tiếp của lớp Command được sử dụng nó đóng vai trò là lớp ConcreteCommand trên mô hình UML chuẩn (hình đầu tiên)
  • Trong Document EditCommand sẽ có các Receiver là Document để thể hiện sự thay đổi trong từng giai đoạn Request.

Cài đặt:

 //base command  
 abstract class Command  
 {  
   abstract public void Redo();  
   abstract public void Redo();  
 }  

 //concrete implementation  
 class DocumentEditCommand : Command  
 {  
      private Document _editableDoc;  
      private string _text;  
      public DocumentEditCommand(Document doc, string text)  
      {  
           _editableDoc = doc;  
           _text = text;  
           _editableDoc.Write(_text);  
      }  
      override public void Redo()  
      {  
           _editableDoc.Write(_text);  
      }  
      override public void Undo()  
      {  
           _editableDoc.Erase(_text);  
      }  
 }  


 //invoker  
 class DocumentInvoker  
 {  
      private ArrayList _commands = new ArrayList();  
      private Document _doc = new Document();  
      public void Redo(int level)  
      {  
           Console.WriteLine("---- Redo {0} level", level);  
           ((Command) _commands[level]).Redo();  
      }  
      public void Undo(int level)  
      {  
           Console.WriteLine("---- Undo {0} level", level);  
           ((Command) _commands[level]).Undo();  
      }  
      public void Write(string text)  
      {  
           DocumentEditCommand cmd = new DocumentEditCommand(_doc, text);  
           _command.Add(cmd);  
      }  
      public string Read()  
      {  
           return _doc.ReadDocument();  
      }  
 }  

Việc cài đặt xong ... ta tiến hành chạy và kiểm tra kế quả có đúng với mong muốn hay không


 DocumentInvoker instance = new DocumentInvoker();  
 instance.Write("This is the original text.");  
   
 //---------Result--------------  
 // This is the original text. --first write  
   
 instance.Write(" Here is some other text.");  
   
 //---------Result---------------  
 // This is the original text. Here is some other text. --second write  
   
 instance.Undo(1);  
   
 //----------Result---------------  
 // ---- Undo 1 level  
 // This is the original text.  
   
 instance.Redo(1);  
   
 //----------Result----------------  
 // ---- Redo 1 level  
 // This is the original text. Here is some other text.  
   
 instance.Write(" And a little more text.");  
 instance.Undo(2);  
 instance.Redo(2);  
 instance.Undo(1);  
   
 //----------Result-----------------  
 // This is the original text. Here is some other text. And a little more text  
 // ----Undo 2 level  
 // This is the original text. Here is some other text.  
 // ----Redo 2 level  
 // This is the original text. Here is some other text. And a little more text.  
 // ---- Undo 1 level  
 // This is the original text. And a little more text.  
   


Kết luận : Mẫu Command giúp chúng ta giải quyết vấn đề xử lý các sự kiện 1 cách tuần tự lần lượt và có thể lưu log... Nó khá giống với mẫu Chain of Responsibility ở chỗ đều thực hiện các công việc một cách tuần tự nhưng cải tiến hơn ở cách quản lý tập trung theo 1 list và có thể quay lại về được bất kì bước nào ở phía trước.










Chain of Responsibility Pattern

dạo này thích viết bằng html hơn là dùng RichText nên mọi người thông cảm >:)

Nếu các bạn đã từng nghe qua về 23 mẫu Design Pattern của nhóm GOF thì thật tốt vì hôm nay chúng ta sẽ nói về mẫu lập trình trong 23 mẫu đó trong 1 bộ môn có tên là "Kiến trúc phần mềm" do Tiến Sỹ Trần Minh Triết đang giảng dạy...

23 mẫu Design Pattern thường dùng được chia thành 3 "phân vùng" gồm
  • Creational Patterns: các mẫu thiết kế thiên về tạo lập và quản lý tạo lập đối tượng
  • Behavioral Patterns: các mẫu thiết kế thiên về phương thức xử lý và quan hệ giữa các class
  • Structural Patterns: các mẫu thiết kế liên quan đến kiến trúc của phần mềm

Ngày hôm nay tạm gác qua các mẫu về Creational Patterns (sẽ được trình bày ở dịp khác) chúng ta đi vào phần Behavioral Patterns.

Như đã trình bày ở trên, các mẫu thiết kế liên quan đến behavioral patterns là những bài giải mẫu liên quan đến việc điều khiển các luồng xử lý và mối liên hệ giữa các class ... Bắt đầu luôn chứ không dài dòng nữa.


Mẫu số 1: Chain of Responsibility Pattern

Chain of Responbility để làm gì???
Giống như tên gọi của nó ... Chain of Responsibility là một mẫu thiết kế giải quyết cho việc thực hiện 1 chuỗi các tác vụ có trình tự mà mỗi 1 tác vụ trong chuỗi đó được đảm nhiệm bởi 1 class.

Ví dụ :
Mua bột --> Nhào bột --> Nướng bánh --> Nhậu

Đó là ví dụ đơn giản về 1 chuỗi các công việc. Mỗi công việc ở trên do một người đảm nhiệm:

phụ việc : đi chợ
phụ bếp : nhào bột
Đầu bếp : Nướng bánh
Thực khách : nhậu

Trong đó Phụ việc, phụ bếp, đầu bếp, thực khách là các class và Đi chợ(), nhào bột(), Nướng bánh(), nhậu() là các phương thức (method).

Hãy tưởng tượng đến 1 sợi dây xích có nhiều mắc xích ...

Trong mẫu Chain of Responsibility chúng ta có một thành phần chính là Handler. Handler là đối tượng đóng gói phương thức xử lý của instances này vào 1 instances khác (xem phần sau sẽ rõ)



Tạm quên UML của nó 1 bên vì nó không cung cấp nhiều thông tin lắm về mẫu này ... ta đến với vấn đề mẫu...

Vấn đề: Một nhóm các class xử lý theo lượt nhưng không có cách nào xác định thứ tự cái nào sẽ được gọi trước.

Ví dụ 1 luồng công việc như sau

 Process firstProcess = new FirstProcess();  
 fistProcess.Run();  
 Process secondProcess = new SecondProcess();  
 secondProcess.Run();  
 Process thirdProcess = new ThirdProcess();  
 thirdProcess.Run();  

Trong đó Process là lớp cha ... các lớp FirstProcess, SecondProcess, ThirdProcess() là các lớp kế thừa.

Giải quyết: Sử dụng một chuỗi class liên hợp để xử lý và định nghĩa bước thực hiện tiếp theo trong hàm xử lý của các class này.

Sơ đồ UML


Chúng ta đặt class thực thi tiếp theo vào class hiện tại, cho phép mỗi class chứa thể hiện (instance) của class tiếp theo trong chuỗi mắc xích.

#RunNext() bao gồm Run() và SetNextProcess()... 2 hàm này sẽ đảm bảo tại mỗi một mắc xích nó sẽ làm đúng nhiệm vụ của mình và ủy thác phần còn lại cho "mắc xích" tiếp theo cho đến khi hết.

Code cài đặt :


 abstract class Process  
 {  
   private Process _nextProcess;  
   protected abstract void RunNext();  
   public void Run()  
   {  
      RunNext();  
      if(_nextProcess != null)  
      {  
        _nextProcess.Run();  
      }  
   }  
   public void SetNextProcess(Process process)  
   {  
      _nextProcess = process;  
   }  
 }  


 class FirstProcess : Process  
 {  
   Protect override void RunNext()  
   {  
     System.Threading.Thread.Sleep(1000);  
   }  
 }  
 class SecondProcess: Process  
 {  
   Protect override void RunNext()  
   {  
     System.Threading.Thread.Sleep(2000);  
   }  
 }  
 class ThirdProcess: Process  
 {  
   Protect override void RunNext()  
   {  
     System.Threading.Thread.Sleep(3000);  
   }  
 }  

Thực thi vấn đề

 Process firstProcess = new FirstProcess();  
 Process secondProcess = new SecondProcess();  
 Process thirdProcess = new ThirdProcess();  
 firstProcess.SetNextProcess(secondProcess);  
 secondProcess.SetNextProcess(thirdProcess);  
 thirdProcess.SetNextProcess(null);  
 firstProcess.Run();  

Kết quả:

 Beginning first process ....  
 Ending first process ....  
 Beginning second process ....  
 Ending second process ....  
 Beginning third process ....  
 Ending third process ....  

Vậy ta đã thực thi được 1 chuỗi các hành động theo mắc xích bằng cách "chỉ ra mắc xích" tiếp theo của mắc xích hiện tại... Đó cũng là tinh thần của Chain of Responbility...

Thứ Ba, 7 tháng 8, 2012

Python trong Eclipse

    Eclipse có một editor mạnh có hỗ trợ khá nhiều loại plugin, trong đó có plugin hỗ trợ cho việc lập trình với python, đó chính là pydev.

    Trước khi cài đặt pydev, bạn nên cài đặt python trước đã.
    Bạn muốn cài pydev, bạn có thể vào trang chủ của pydev hoặc lười quá thì xem ở đây cho tiện :p
=================================================

Cài đặt:


Mở eclipse:
     Help >> Install New Softwares... >> Trong khung work with, bạn bấm Add, sẽ hiện ra hộp thoại, trong khung name bạn điền tên bất kì giúp bạn dễ nhớ, vd mình điền vào là PyDev, rồi copy dòng này vô khung location : http://pydev.org/updates (Nếu không được thì copy dòng này vô khung location : http://pydev.org/nightly )

    OK

    Check vào checkbox PyDev rồi next, next, accept gì đó rồi finish


Xong chưa nhỉ ... Còn 1 tí :-p

Trong eclipse:
    Windows >> Preferences >> PyDev >> Interpreter - Python >> Autoconfig (hoặc bạn có thể chọn new rồi chọn tới file python.exe trong windows hoặc /usr/bin/pythonx.x trong linux)

    Apply

    OK

Thế là bạn đã cài đặt xong
===================================================

Còn 1 khâu nữa là sử dụng, how how... ???

Bạn để ý ở góc phải eclipse có cái nút Open Perspective, hoặc bạn có thể vào Windows >> Open Perspective . Sau đó bạn chọn Pydev. Khung nhìn dành cho pydev xuất hiện.

Tạo project: khi tạo project, sẽ có lựa chọn PyDev project, bạn hãy chọn nó ^^
(Alt + Shift + N : phím tắt tạo project)

Tạo module: khi tạo một file mới, sẽ có lựa chọn Module hay package, bạn có thể tùy chọn theo ý ^^  

Trong lúc code, khi 1 hàm hay 1 lớp nào đó chưa có, 1 chỉ cần để con trỏ lên tên của hàm hay lớp đó, bận ctrl + 1 để tạo. Tương tự cho import module.

Nếu muốn thay đổi PYTHONPATH, bạn có thể click phải lên project, chọn mục PYTHONPATH và thêm những thư mục cần thiết.

Thứ Bảy, 4 tháng 8, 2012

1.2 Widget (Part 2)

Untitled Document

Hệ tọa độ

Kivy sử dụng hệ tọa độ 2D giống trong sách giáo khoa mà mọi người đã từng học. Với gốc tọa độ (0,0) nằm ở góc trái, phía dưới của màn hình. Trục x nằm ngang, hướng về bên phải, trục y đứng, hướng lên trên.

Dưới đây sẽ là 1 số loại layout thông dụng

Float Layout

The FloatLayout sử dụng chủ yêu cho 2 thuộc tính: pos_hint và size_hint.
Mỗi widget đều có size_hint = 1. size_hint = 1: nghĩa là kích cỡ của widget đó bằng với kích cỡ của widget cha. pos_hint là thuộc tính kiểu Dictionary, hẳn bạn đã biết kiểu Dictionary của python. vd: pos_hint={'x':.02, 'y':.01}, ngoài 2 key 'x' và 'y' ra, pos_hint còn có 'right', 'top', 'center_x', 'center_y', bạn sẽ sử dụng khi cần thiết.
Vậy, đối với mỗi widget con của Float layout, bạn chỉ cần thiết lập 2 thuộc tính pos_hint và size_hint để xác định vị trí.
Bạn có thể xem thêm trong đây để biết thêm chi tiết: link

Box Layout

Đây là 1 loại layout khá phổ biến đối với các ứng dụng giao diện. Nếu bạn chưa từng biết qua, thì cũng không khó để tiếp cận. Box layout đơn giản chỉ phục vụ cho việc sắp sếp các widget con theo chiều dọc, hoặc theo chiều ngang, có thể kếp hợp nhiều Boxlayout chồng nhau để tạo giao diện phức tạp tùy ý.
Cụ thể, đối với Box layout theo chiều dọc, các widget con sẽ được sắp xếp như sau:

Bạn có thể xem thêm trong đây để biết thêm chi tiết: link

1.2 Widget (Part 1)

Widget

Như đã nói ở bài trước, ở bài này chúng ta sẽ tìm hiểu về widget.

Widget là gì?

Widget đơn giản chỉ là một đối tượng tương tác giao diện, chẳng hạn như button, slider,...
Trong Kivy, các widget này các bạn sẽ tìm thấy trong module : kivy.uix.<tên widget>
Ở bài trước, chúng ta đã sử dụng widget button, các bạn có thể làm tương tự với các widget khác

Vấn đề đặt ra?

Ở ví dụ trước, ta chỉ xài 1 widget, vậy muốn xài nhiều widget cùng lúc ta phải làm sao?
Do đó Kivy hỗ trợ hệ thống cây widget (Widget Tree).
Bạn chỉ cần hiểu cây widget nghĩa là: sẽ có 1 widget gốc, rồi từ widget đó, bạn add thêm widget con. Từ những widget con đó, bạn có thể add thêm nữa tùy thích.

Các hàm cơ bản của hệ thống cây widget

add_widget(): add a widget as a child
remove_widget(): remove a widget from the children list
clear_widgets(): remove all children from a widget

Layout

Trong số các widget, có 1 widget tên là Layout. Đây là đối tượng đặc biệt, được sử dụng trong việc gom nhóm các widget, và phân bổ vị trí của các widget.
Tuy nhiên, nếu bạn sử dụng lớp Layout thì bạn sẽ gặp phải exception thông báo không sử dụng được. Lí do: Layout là lớp trừu tượng, không được sử dụng (trong python không có khái niệm này, chỉ là do người lập trình tạo ra exception để các lập trình viên biết đây là lớp trừu tượng để tránh sử dụng)
Một số layout có thể sử dụng được:
- AnchorLayout
- BoxLayout
- FloatLayout
- GridLayout
- StackLayout
- TabbedPanel