Giáo trình Lập trình nâng cao (Phần 2) - Nghề: Lập trình máy tính - Trình độ: Cao đẳng nghề

pdf 169 trang Gia Huy 17/05/2022 1720
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Lập trình nâng cao (Phần 2) - Nghề: Lập trình máy tính - Trình độ: Cao đẳng nghề", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfgiao_trinh_lap_trinh_nang_cao_phan_2_nghe_lap_trinh_may_tinh.pdf

Nội dung text: Giáo trình Lập trình nâng cao (Phần 2) - Nghề: Lập trình máy tính - Trình độ: Cao đẳng nghề

  1. BÀI 6 TƯƠNG TÁC GIỮA NGƯỜI DÙNG VỚI ỨNG DỤNG Mã bài : ITPRG03.6 Giới thiệu : Trong bài học này chúng ta sẽ nghiên cứu phương pháp tạo sự tương tác dễ dàng cho người sử dụng với chương trình bằng cách sử dụng các công cụ hiện có trong Borland C++ Builder 6.0. Mục tiêu thực hiện: Học xong bài này học viên sẽ có khả năng: Sử dụng một số hộp thoại của C++ Builder. Sử dụng các hộp thoại dùng chung của Windows. Biết tạo và quản lý các hộp thoại. Mô tả các mô hình giao diện ứng dụng người dùng của Windows. Nội dung chính: 6.1 Sử dụng các hộp thoại Các hộp thoại Windows: Một hộp thoại là một cửa sổ dùng để tương tác với máy tính. Bản thân hộp thoại không mang một ý nghĩa gì. Các điều khiển nằm trên nó thực hiện luật tương tác giữa người sử dụng và máy tính. Trước tiên, một hộp thoại có những đặc trưng sau đây: Nó được trang bị một nút nhấn Close . Nút nhấn này cho phép người sử dụng hủy bỏ hộp thoại. Thông thường, nút nhấn này sẽ được cấu hình giống như người sử dụng nhấn Cancel hoặc nhấn phím Esc. Nó không thể thu nhỏ, phóng to, hay trả lại trạng thái ban đầu (restored). Một hộp thoại không có nút hệ thống nào khác ngoài nút Close. Nó là modal. Người sử dụn thường không cho phép tiếp tục các hành động khác cho đến khi anh ta đóng hộp thoại này. Nó cung cấp một cách cho người sử dụng đóng hay hủy bỏ hộp thoại. Đa số hộp thoại có một nút Ok và một nút Cancel, tùy thuộc trên ứng dụng phát triển. Khi các hộp thoại có nút Ok và Cancel, nút Ok được cấu hình để có thể kích hoạt bằng phím Enter; nút Cancel được kích hoạt bằng phím Esc. Hộp thoại thông điệp (Message Boxes) Hình 88-Hộp thoại minh họa 134
  2. Một hộp thoại thôn điệp là một hộp thoại nhỏ dùng để hiển thị một thông điệp và cung cấp một hay nhiều nút nhấn. Nó thường dùng để cung cấp một thông tin tới người sử dụng hay yêu cầu một quyết định từ người sử dụng. Bằng cách nhấn một trong những nút nhấn, người sử dụng tạo ra một quyết định và chương trình tiếp tục. Các hộp thoại thông điệp được tạo từ một hàm dựng sẵn từ VCL và thư viện Win32. Chúng ta tiến hành chuẩn bị để tạo một ứng dụng mẫu về các hộp thoại thông điệp theo các bước sau: Tạo một dự án mới với form mặc định của nó. Đổi thuộc tính caption của form thành Message Boxes Configurations Từ ngăn công cụ Standard, chọn đối tượng Edit và đặt nó lên Form. Đổi tên của đối tượng này thành edtMessage và xóa nội dụng trong thuộc tính Text. Hàm ShowMessage Hàm ShowMessage() cung cấp các hộp thoại cơ bản nhất của Borland. Hàm này có một tham số chuỗi và không trả về bất cứ giá trị nào. Nó được sử dụng để hiển thị một thông điệp đến người sử dụng và người sử dụng nhấn nú Ok để chấp nhận. Cú pháp của hàm ShowMessage() là: void __fastcall ShowMessage(const AnsiString Message); Một hộp thoại thông điệp được tạo ra với hàm ShowMessage() sử dụng tên của dự án làm tiê đề của nó. Thông điệp để hiển thị là một chuỗi được cung cấp bởi người sử dụng. Đây là một ví dụ: // void __fastcall TForm1::btnShowMsgClick(TObject *Sender) { ShowMessage("Welcome to the Sellers Bank."); } // Chuỗi này cũng có thể dẫn xuất từ một đối tượng khác chẳng hạn như nội dung của một hộp thoại thảo, một vùng nhớ (memo) hoặc bất kỳ đối tượng văn bản nào khác. Đây là một ví dụ: // void __fastcall TForm1::btnMsgFromEditClick(TObject *Sender) { ShowMessage(edtMessage->Text); } // Chuỗi này cũng có thể là một sự ghép nối nhiều chuỗi khác nhau: // void __fastcall TForm1::Button1Click(TObject *Sender) { ShowMessage("The name " + AnsiString("\"") + 135
  3. edtMessage->Text + AnsiString("\"") + " is not in our records."); } // Sử dụng hàm ShowMessage() Từ ngăn công cụ Standard, chọn đối tượng Button và đặt nó lên form. Thay đổi thuộc tính của Button thành Show &Msg và đổi tên của nó thành butnShowMsg. Nhấn đôi chuột lên nút butnShowMsg để viết mã lệnh cho sự kiện Click Nhấn phím tab và cài đặt đoạn mã lệnh sau: // void __fastcall TForm1::btnShowMsgClick(TObject *Sender) { ShowMessage("Please fill out your Time Sheet before leaving."); } // Để kiểm thử chương trình, nhấn phìm F9. Nhấn vào nút Show Msg: Hình 89-Hộp thoại hiển thị khi nấn vào nút Show Msg Nhấn Ok để đóng form . Để lưu dự án, trên thực đơn chính, chọn File/Save All Tìm thư mục để lưu ví dụ này. Nhấn nút Create new Folder. Gõ Message Boxes và nhấn phím Enter hai lần để hiển thị thư mục mới trong hộp đổ xuống Save. Nhấn nút Save để lưu Unit. Gõ Messages để thay tên của dự án và nhấn Enter Để hiển thị thông điệp nhiều hơn một dòng, thay đổi mã lệnh thành như sau: // void __fastcall TForm1::btnShowMsgClick(TObject *Sender) { ShowMessage("Please fill out your Time Sheet before leaving.\n" "Make sure you sign and send it to Human Resources."); } // 136
  4. Chạy chương trình và xem kết quả: Hình 90-Hộp thoại nhiều dòng Hàm MessageBox Hàm MessageBox() được dẫn xuất từ Win32. Cú pháp của nó là: int __fastcall MessageBox(const char * Message, const char * Caption, int Flags); Hình 91-Minh họa các tham số của hàm MessageBox() MessageBox() được tạo ra với ba tham số. Tham số đầu tiên, Message, là một chuỗi kết thúc rỗng chỉ định thông điệp mà người sử dụng sẽ đọc. Chuỗi Message có thể là một câu tĩnh. Nó có thể được kiếm tạo từ một đối tượng khác. Hoặc nó có thể là một sự kết hợp của nhiều chuỗi khác nhau bằng cách sử dụng các hàm và phép toán chuỗi C/C++. Chúng ta có thể tạo một hộp thoại thông điệp đơn giản tương tự như việc sử dụng hàm ShowMessage() để hiển thị một hộp thoại thông điệp đơn giản với nút OK. Trong trường hợp này, chúng ta chỉ chần cung cấp một tham số duy nhất Message. Cài đặt hai tham số còn lại là NULL. Đây là ví dụ: // void __fastcall TForm1::Button1Click(TObject *Sender) { Application->MessageBox( "This operation can only be " "performed by an administrator.", NULL, NULL); } // Tham số thứ 2, cũng là một chuỗi, là tiêu đề sẽ hiển thị trên thanh tiêu đề của hộp thoại. Chúng ta cũng có thể cài đặt nó khi tạo hộp thoại thông điệp hay chúng ta có thể xây dựng nó vào lúc thực thi. Nếu chúng ta không có tiêu đề, chúng ta có thể cài đặt giá trị của tham số này thành NULL. Trong trường hợp này, tiêu đề sẽ hiển thị thành Error. Do đó, để tạo một hộp thoại ít buồn chán, cung cấp tham số Caption. Đây là ví dụ: // 137
  5. void __fastcall TForm1::Button1Click(TObject *Sender) { Application->MessageBox("Make sure the music is playing.", "CD PLayer Instructions", NULL); } // Tham số thứ ba chỉ định các cờ chúng ta muốn hiển thị trên hộp thoại: một hay nhiều nút nhấn và một số hình ảnh tùy chọn. Chúng ta cũng có thể tạo một hộp thoại đơn giản với một nút duy nhất OK. Trong trường hợp đó, cài đặt tham số thứ ba thành MB_OK. Đây là ví dụ: // void __fastcall TForm1::Button1Click(TObject *Sender) { Application->MessageBox("Make sure the music is playing.", "CD PLayer Instructions", MB_OK); } // Để hiển thị nhiều hơn một nút nhấn, sử dụng các hằng số nguyên để ấn định một nhóm các nút cho phép. Sau đây là các hằng số và các nút nhấn của nó: Hằng số nguyên Các nút nhấn MB_OK MB_OKCANCEL MB_ABORTRETRYIGNORE MB_YESNOCANCEL MB_YESNO MB_RETRYCANCEL MB_CANCELTRYCONTINUE MB_HELP Ví dụ, để tạo một hộp thoại hiển thị nút Yes va nút No, chúng ta có thể viết: // void __fastcall TForm1::Button1Click(TObject *Sender) { Application->MessageBox("Do you hear any music now or any sound at all?", 138
  6. "CD Player Instructions", MB_YESNO); } // Nếu chung ta cung cấp MB_HELP như một nút nhấn duy nhất, hộp thoại sẽ hiển thị với một nút OK và một nút Help. Chúng ta có thể hiển thị một biểu tượng bằng các sử dụng một trong các hằng số nguyên Win32. Mặc dù, chúng ta có thể sử dụng bất kỳ biểu tượng với bất kỳ nút nhấn nào, chúng ta phải khéo trong việc hiển thị biểu tưởng tương ứng với ngữ cảnh của thông điệp. Các giá trị của các biểu tượng được liệt kê: Giá trị Biểu Thích hợp khi tượng MB_ICONEXCLAMATION Cảnh báo người sử dụng một hành động MB_ICONWARNING thi hành trên ứng dụng MB_ICONINFORMATION Thông báo người sử dụng một trạng thái MB_ICONASTERISK không phê phán MB_ICONQUESTION Hỏi một câu hỏi với câu trả lời: Yes hoặc No hay Yes, No hoặc Cancel MB_ICONSTOP Một tình huống phê phán hay có lỗi xảy ra. MB_ICONERROR Biểu tượng này tương ứng khi thông tin cho người sử dụng một sự kết thúc hay một sự từ chối một hành động. Các biểu tượng này được sử dụng chung với các hằng số nút nhấn. Để kết hợp các cờ này, sử dụng phép toán bit OR “|”. Đây là một ví dụ: // void __fastcall TForm1::Button1Click(TObject *Sender) { Application->MessageBox("Do you hear any music now or any sound at all?", "CD Player Instructions", MB_YESNOCANCEL | MB_ICONQUESTION); } // Khi một hộp thoại được cấu hình để hiển thị một hay nhiều nút nhấn, hệ điều hành sẽ cài đặt quyết định nút nào là nút mặc định. Nút mặc định sẽ có một đường viền đậm và được đặt tách rời với các nút khác. Nếu người sử dụng nhấn phím Enter, hộp thoại sẽ phát sinh sự kiện như người sử dụng nhấn nút trên nút mặc định. May mắn thay, nếu hộp thoại có nhiều hơn một nút, chúng ta có thể quyết định nút nhấn 139
  7. nào sẽ trở thành mặc định. Để chỉ định nút mặc định, sử dụng một trong những hằng số sau: Giá trị Nếu hộp thoại có nhiều nút nhấn, nút nhấn mặc định sẽ là MB_DEFBUTTON1 Nút đầu tiên MB_DEFBUTTON2 Nút thứ hai MB_DEFBUTTON3 Nút thứ ba MB_DEFBUTTON4 Nút thứ tư Để chỉ định nút nhấn mặc định, sử dụng phép toán bit OR để kết hợp các hằng số nguyên để chỉ định nút nhấn mặc định với các hằng số nút nhấn và biểu tượng. Sau đây là một ví dụ: // void __fastcall TForm1::Button1Click(TObject *Sender) { Application->MessageBox("Do you hear any music now or any sound at all?", "CD Player Instructions", MB_YESNOCANCEL | MB_ICONQUESTION | MB_DEFBUTTON2); } // Sau khi đọc thông điệp hiển thị trên một hộp thoại, người sử dụng sẽ nhấn một trong những nút nhấn và hộp thoại sẽ đóng lại. Mỗi một nút nhấn có một hằng số nguyên được gán và đồng bộ bởi trình dịch. Chúng ta có thể sử dụng các giá trị này để tìm xem nút nhấn nào đã được nhấn. Điều này có nghĩa là hàm MessageBox() trả về một giá trị số nguyên được liệt kê trong bảng sau: MessageBox() trả về Nếu người sử dụng nhấn IDOK OK hoặc phím Enter IDCANCEL Cancel hoặc phím Esc IDABORT Abort IDRETRY Retry IDIGNORE Ignore IDNO No IDYES Yes IDCONTINUE Continue IDTRYAGAIN Try Again 140
  8. Do đó, chúng ta có thể sử dụng một trong những giá trị nguyên này để hành động phụ thuộc trên nút nhấn được nhấn: // void __fastcall TForm1::Button1Click(TObject *Sender) { if( Application->MessageBox( "Do you hear any music now or any sound at all?", "CD Player Instructions", MB_YESNOCANCEL | MB_ICONQUESTION) == IDNO ) Panel1->Caption = "We will stop these tests now. " "Let the machine rest!"; } // Sử dụng hàm MessageBox() Thêm đối tượng Button lên form. Đổi Caption của đối tượng Button thành Simple &1 và tên của nó thành btnSimpleMsg. Nhấn đôi chuột lên nút nhấn để viết mã lệnh cho sự kiện Click Nhấn phím Tab và gõ: Application->MessageBox("This operation can only be performed by an administrator.", NULL, NULL); Để kiểm thử form, nhấn phím F9. Nhấn nút Simple 1 để xem hộp thoại thông điệp. Chú ý rằng hộp thoại có một nút nhấn OK và một tiêu đề Error: Hình 92-Minh họa kết quả sử dụng hàm MessageBox() Nhấn OK và đóng form. Nhấn phím F12 để hiển thị form. Thêm một nút nhấn khác vào form. Đổi tiêu đề (caption) của nó thành Simple &2 và tên của nó thành btnSimple2. Nhấn đôi chuột vào nút Simple 2. Nhấn Tab và gõ: 141
  9. Application->MessageBox("Make sure the music is playing.", "CD PLayer Instructions", MB_OK); Kiểm thử form và về lại Borland C++ Builder. Thêm một nút nhấn khác vào form. Đổi tiêu đề của nó thành &Question và tên của nó thành btnQuestion. Nhấn đôi chuột lên nút Question. Nhấn phím Tab và gõ: Application->MessageBox("Do you hear any music now or any sound at all?", "CD Player Instructions", MB_YESNOCANCEL | MB_ICONQUESTION); Kiểm thử form: Hình 93-Hộp thoại Question Trở lại Borland C++ Builder. Thêm một nút nhấn khác vào Form. Đổi Caption thành &Default và tên của nó thành btnDefault. Nhấn đôi chuột và cài đặt đoạn mã sau: // void __fastcall TForm1::btnDefaultClick(TObject *Sender) { Application->MessageBox( "The file you are trying to copy is being " "used by someone else.\n" "Would you like to try later? If you click\n" "Yes: You will be reminded when the file is ready.\n" "No: Copy the file anyway. You will get only the source file\n" "Cancel: Cancel the operation.", "Copying Files", MB_YESNOCANCEL | MB_ICONQUESTION | MB_DEFBUTTON2); } // Kiểm thử Form: 142
  10. Hình 94-Hộp thoại có nút nhấn mặc định Trở lại Borland C++ Builder Hàm MessageDlg: Hình 95-Minh họa kết quả của hàm MessageDlg Hàm MessageDlg() là một hộp thông điệp tinh chỉnh của Borland và nó cung cấp một sự thay thế tốt cho hàm MessageBox() của Win32. Cú pháp của hàm MessageDlg() là: int __fastcall MessageDlg(const AnsiString Message, TMsgDlgType IconType, TMsgDlgButtons Buttons, int HelpContext); Tham số đầu tiên, Message, là thông điệp gởi đến người sử dụng. Nó có thể là một câu đơn giản, một đoạn hay sự kết hợp nhiều chuỗi. IconType là một biểu tượng được sử dụng để làm nổi bật hộp thoại. Biểu tượng này được cài đặt bằng cách sử dụng một hằng số nguyên trong số các hằng sau: Giá trị Biểu tượng mtWarning mtError mtInformation mtConfirmation mtCustom None Tham số Buttons được sử dụng kiểu của nút nhấn sẽ hiển thị trong hộp thoại. Các nút nhận được định nghĩa sử dụng tập hợp TMsgDlgButtons như sau: Giá trị Nút nhấn Giá trị Nút nhấn 143
  11. mbYes mbRetry mbNo mbIgnore mbOK mbAll mbCancel mbNoToAll mbAbort mbYesToAll mbHelp Tham số cuối cùng được sử dụng nếu một tập tin hướng dẫn được cho phép, trong trường hơp này chúng ta muốn chỉ định một phần chỉ mục liên quan đến hộp thoại này. Hộp thoại này sử dụng tên của dự án làm tiêu đều của nó. Sử dụng hàm MessageDlg: Thêm một nút nhấn vào form. Thay đổi tiêu đề của nó thành Msg Di&alog 1 và tên của nó hành btnMsgDialog1. Nhấn đôi chuột lên nút nhấn Msg Dlg1. Nhấn phím Tab và gõ: MessageDlg("All songs on the CD have been copied. Now it will be ejected.", mtInformation, TMsgDlgButtons() Text + AnsiString("\"") + " that you are trying to upload " "already exists on the server.\n" "If you continue, you will replace the recent versions " "of the files on the server.\n" "Would you like to upload anyway?", mtConfirmation, TMsgDlgButtons() << mbNoToAll << mbNo << mbYes << mbYesToAll, 0); } 144
  12. // Để kiểm thử form, nhấn F9. Trong hộp văn bản hộp thoại, chúng ta gõ canonderby.asp Nhấn chuột lên nút Msg Dialog 2: Hình 96-Kết quả hiển thị của hàm MessageDlg() Trở lại Borland C++ Builder Hộp thoại nhập liệu InputBox() cho phép chúng ta hiển thị một hộp thông điệp yêu cầu một thông tin từ người sử dụng. Hộp thoại này được trang bị một hộp văn bản và hai nút nhấn. Hộp văn bản chấp nhận một chuỗi từ người sử dụng. Người sử dụng có thể gõ bất kỳ văn bản nào. Khi người sử dụng nhấn nút OK, hộp nhập liệu sẽ trả về nội dung của hộp văn bản của nó. Nếu người sử dụng nhấn Cancel hoặc phím Esc, nội dun của hộp văn bản sẽ được hủy bỏ. Cú pháp của hàm InputBox() là AnsiString __fastcall InputBox(const AnsiString Caption, const AnsiString Prompt, const AnsiString Default); Tham số Caption chỉ định chuỗi sẽ hiển thị trên thanh tiêu đề của hộp thoại. Prompt là một câu sẽ hiển thị đến người sử dụng để yêu cầu thông tin gõ vào hộp soạn thảo. Tham số Default là một giá trị gợi ý chúng ta, nó có thể hiển thị trong hộp soạn thảo để hướng dẫn người sử dụng kiểu của giá trị mong chờ. Nếu chúng ta không muốn chỉ định một giá trị mặc định, chúng ta có thể cài đặt giá trị của nó thành rỗng. Sau đây là ví dụ: // void __fastcall TForm1::btnInputClick(TObject *Sender) { InputBox( "Temperature Conversion", "Type the temperature in Celsius:", ""); } // Mặc dù tham số Default là một chuỗi, chúng ta có thể khởi tạo nó bằng bất kỳ giá trị nào mà lớp AnsiString có thể sử dụng trong kiến tạo của chúng. Có nghĩa là giá trị default có thể là một ký tự, một số thực hay một số nguyên: // void __fastcall TForm1::btnInputClick(TObject *Sender) 145
  13. { InputBox( "Temperature Conversion", "Type the temperature in Celsius:", 102); } // Nếu chúng ta chỉ định tham số Default và người sử dụng nhấn nút OK mà không thay đổi nội dung của hộp soạn thảo, trình dịch phải sẽ quyết định giá trị mặc định như giá trị hợp lệ Sau khi sử dụng hộp thoại, người sử dụng sẽ nhấn OK, nhấn phím Enter, nhấn Cancel hay phím Esc. Nếu người sử dụng nhấn Ok hay nhấn phím Enter, hàm trả về giá trị mà người sử dụng đã gõ vào trong hộp soạn thảo. Điều này cho phép chúng ta viết một lệnh điều kiện để kiểm tra giá trị mới được trả về bởi việc nhấn nút Ok trên hộp thoại InputBox: // void __fastcall TForm1::btnInputClick(TObject *Sender) { Edit1->Text = InputBox("Distance and Measurement", "Enter the distance in kilometer:", 0.00); } // Nếu người sử dụng nhấn Cancel hay phím Esc, những gì hiển thị trong hộp soạn thảo sẽ được bỏ qua. Nhưng hàm sẽ trả về giá trị mặc định được truyền vào tham số Default. Nếu giá trị trả về dự định cho việc tính toán toán học, ngày, hay giờ, chúng ta phải chuyển nó sang dạng tương ứng. Sử dụng hộp thoại InputBox: Thêm một nút nhấn vào form. Đổi tiêu đề của nó thành Input &Box và tên của nó thành btnInputBox. Nhấn đôi chuột lên nút Input Box. Nhấn Enter và gõ: InputBox("Student Registration", "Type the Student's Gender", ""); Nhấn F9 để kiểm thử form. Nhấn nút InputBox. Chú ý rằng nội dụng của hộp soạn thảo của nó là rỗng. Đóng Input Box và form. Cung cấp một giá trị mặc định cho người sử dụng, thay đổi nội dung của hàm trước như sau: InputBox("Student Registration", "Type the Student's Gender", "Male"); Kiểm thử form và nhấn nút Input Box. Để sử dụng nội dung của một đối tượng khác, chẳng hạn như một hộp soạn thảo, như giá trị mặc định, thay đổi đoạn mã trên như sau: 146
  14. InputBox("Music Collection", "Enter the category of music to register", edtMessage- >Text); Để kiểm thử form, nhấn phím F9. Gõ Salsa vào hộp soạn thảo Message. Nhấn nút Input Box và chú y rằng giá trị mặc định của nó là giá trị đã gõ vào hộp soạn thảo: Hình 97-Hộp thoại InputBox có giá trị mặc định Gõ Irish Folks và nhấn OK Đóng form. Để sử dụng và áp dụng kết quả của một Input Box vào một đối tượng khác, chẳng hạn, để dá nó vào trong một hộp soạn thảo, sửa đoạn mã trên thành: edtMessage->Text = InputBox("Employees Records", "Employee's Department", "Human Resources"); Để kiểm thử form, nhấn F9. Nhấn nút Input Box. n Gõ Accounting và nhấn Enter. Chú ý rằng Accounting hiển thị trong hộp soạn thảo Message. 6.2 Hộp thoại Windows dùng chung Các hộp thoại Windows dùng chung được cung cấp bởi C++ Builder bao gồm: Hộp thoại mở tập tin: Dùng để mở các tập tin, được khai báo trong lớp TOpenDialog. Hộp thoại lưu tập tin: Dùng để lưu tập tin, được khai báo trong lớp TSaveDialog. Hộp thoại mở tập tin hình ảnh: Tương tự như hộp thoại mở tập tin nhưng dùng để mở các tập tin hình ảnh, được khai báo trong TOpenPictureDialog. Hộp thoại lưu tập tin hình ảnh: Tương tự như hộp thoại lưu tập tin nhưng dùng để lưu các tập tin hình ảnh, được khai báo trong lớp TSavePictureDialog. Hộp thoại chọn font chữ: Dùng để mở hộp thoại lựa chọn và điều chỉnh font chữ, được khai báo trong lớp TFontDialog. 147
  15. Hộp thoại chọn màu: Dùng để mở hộp thoại chọn màu được khai báo trong lớp TColorDialog. Hộp thoại lựa chọn máy in: Dùng để mở hộp thoại lựa chọn máy in được khai báo trong lớp TPrinterDialog. Hộp thoại cài đặt máy in: Dùng để mở hộp thoại điều chỉnh các thông số về trang in được khai báo trong lớp TPrinterSetupDialog. Hộp thoại tìm kiếm: Dùng để mở hộp thoại tìm kiếm văn bản, được khai báo trong lớp TFindDialog. Hộp thoại tìm kiếm và thay thế: Dùng để mở hộp thoại tìm kiếm và thay thế, được khai báo trong lớp TReplaceDialog. Các hộp thoại này được dùng cho những mục đích khác nhau. Mỗi lập trình viên đều có thể sử dụng các hộp thoại theo chức năng chương trình của mình. 6.3 Tạo và quản lý các hộp thoại Trong mục trên, chúng ta đã xem qua giới thiệu sơ lược về các chức năng của các hộp thoại. Trong nội dung phần này, chúng ta sẽ nghiên cứu ngắn gọn về phương pháp sử dụng các hộp thoại đã liệt kê ở trên. Trước khi đi vào nội dung chi tiết về việc sử dụng các hộp thoại, chúng ta cần phải hiểu được phương thức quan trọng sau: Phương thức Execute(): Phương thức này trả về một trong hai giá trị TRUE hoặc FALSE tương ứng với việc đóng hộp thoại bằng cách sử dụng nút quyết định hay nút hủy bỏ; chẳng hạn nút OK (Open) được gọi là nút quyết định và nút Cancel gọi là nút hủy bỏ. Khi nhấn nút OK, hộp thoại sẽ được đóng lại và trả về giá trị TRUE; khi nhấn nút Cancel, hộp thoại sẽ được đóng lại và trả về giá trị FALSE. Hộp thoại mở tập tin và hộp thoại lưu tập tin: Các thuộc tính quan trọng của hộp thoại mở tập tin được liệt kê trong bảng sau: Thuộc tính Ý nghĩa DefaultExt Phần mở rộng của mặc định khi hộp thoại mở tập tin được mở ra FileName Tên tập tin được chọn trong hộp thoại mở tập tin trong hộp thoại mở tập tin Files Danh sách các tập tin được chọn trong hộp thoại mở tập tin Filter Bộ lọc các phần mở rộng tập tin FilterIndex Chỉ số mặc định của bộ lọc phần mở rộng tập tin InitialDir Thư mục mặc định được sử dụng khi hộp thoại tập tin được mở. 148
  16. Options Thuộc tính điều chỉnh các tùy chọn; chứa danh sách các tùy chọn được liệt kê dưới đây: ofReadOnly: Chọn mở ở chế độ chỉ đọc. ofHideReadOnly: Cho phép hay không cho phép hiển thị nút đánh dấu Open as Read-Only. ofShowHelp: Hiển thị nút Help trên hộp thoại. ofNoValidate: Bỏ đánh dấu cho các ký tự không hợp lệ. Cho phép chọn một tập tin với các ký tự không hợp lệ. ofAllowMultiSelect: Cho phép người sử dụng chọn nhiều tập tin cùng một lúc. ofExtensionDifferent: Cờ này được bật vào lúc thực khi bất cứ khi nào chọn một tập tin có phần mở rộng khác với phần mở rộng mặc định DefaultExt. Nếu chúng ta sử dụng tùy chọn này, chúng ta cần phải cài đặt lại nó. ofPathMustExist: Phát sinh một thông báo lỗi nếu người sử dụng chọn một đường dẫn tập tin không tồn tại. ofFileMustExist: Phát sinh một thông báo lỗi nếu người sử dụng cố gắng chọn một tập tin không tồn tại. ofCreatePrompt: Phát sinh một cảnh báo nếu người sử dụng chọn một tập tin không tồn tại, hộp thoại sẽ hỏi tạo một tập tin mới với tên chỉ định. ofNoReadOnlyReturn: Phát sinh một thông báo lỗi nếu người sử dụng cố gắng chọn một tập tin chỉ đọc. ofEnableSizing: Cho phép hộp thoại có thể được thay đổi kích thước. ofDontAddToRecent: Cấm tập tin thêm vào danh sách các tập tin recently. ofShowHidden: Cho phép hiển thị các tập tin ẩn trong hộp thoại. Title Tiêu đề của hộp thoại. Các ví dụ việc sử dụng mã lệnh để điều khiển các thuộc tính: - Thuộc tính DefaultExt: Thuộc tính này không chấp nhận phần mở rộng nhiều hơn ba ký tự. Phần mở rộng được lưu trữ trong thuộc tính này không bao gồm dấu chấm phía trước. SavePictureDialog1->DefaultExt = GraphicExtension(__classid(Graphics::TBitmap)); SavePictureDialog1->Filter = GraphicFilter(__classid(Graphics::TBitmap)); if (SavePictureDialog1->Execute()) // save the graphic - Thuộc tính FileName, Title: Thuộc tính FileName trả về tập tin bao gồm cả đường dẫn. Thuộc tính Title cho phép gán giá trị tiêu đề cho hộp thoại. Chúng ta hãy xem xét các ví dụ sau: 149
  17. Ví dụ 1: Đoạn mã lệnh sau mở nội dung tập tin và đưa vào một Memo: if (OpenDialog1->Execute()) Memo1->Lines->LoadFromFile(OpenDialog1->FileName); else Memo1->Clear(); Ví dụ 2: Đoạn mã lệnh sau là đoạn mã lệnh cho sự kiện OnClick của Button1 để xóa tập tin được chọn. Trong đoạn mã lệnh này, chúng ta sử dụng hàm FileExists để kiểm tra xem tập tin được chọn có tồn tại hay không rồi tiến hành xóa. void __fastcall TForm1::Button1Click(TObject *Sender) { //Đổi tiêu đề của hộp thoại thành Delete File OpenDialog1->Title = "Delete File"; if (OpenDialog1->Execute()) { if (FileExists(OpenDialog1->FileName)) DeleteFile(OpenDialog1->FileName); } } - Thuộc tính Files, Filter, FilterIndex: Thuộc tính Files có kiểu TStrings chứa danh sách các tập tin. Thuộc tính Filter có kiểu String chứa đoạn văn bản để thực hiện bộ lọc. Thuộc tính này có thể được điều chỉnh bằng cửa sổ Object Inspector. Thuộc tính FilterIndex có kiểu là số nguyên, chỉ định chỉ số mặc định của thuộc tính Filter nhằm chọn Filter lựa chọn. Thuộc tính Options liệt kê các tùy chọn tác động trên hộp thoại. Chúng ta xem xét các ví dụ sau: Ví dụ 1: Đoạn mã lệnh sau sẽ hiển thị tất cả các tập tin vào một ListBox: ListBox1->Items->Assign(OpenDialog1->Files); Ví dụ 2: Đoạn mã lệnh sau dành cho sự kiện OnClick của nút Button1 và đọc dòng đầu tiên của các tập tin để liệt kê vào trong một ListBox. void __fastcall TForm1::Button1Click(TObject *Sender) { FILE *stream; char FirstLine[512]; OpenDialog1->Options.Clear(); //cho phép chọn nhiều tập tin và kiểu tra tập tin được chọn phải tồn tại 150
  18. OpenDialog1->Options Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"; //cho phép bộ lọc *.* có tác dụng khi mở hộp thoại OpenDialog1->FilterIndex = 2; // start the dialog showing all files //mở hộp thoại và kiểm tra xem nhấn nút Open hay không? if (OpenDialog1->Execute()) //nếu nhấn nút Open { //chạy một vòng lặp từ tập tin đầu tiên đến tập tin cuối cùng for (int I = 0; I Files->Count; I ++) { //mở một tập tin với chỉ số tương ứng stream = fopen(OpenDialog1->Files->Strings[I].c_str(), "r"); if (stream) { // đọc dòng đầu tiên của tập tin fgets(FirstLine, sizeof(FirstLine), stream); //thêm dòng này đầu tiên vào Memo Memo1->Lines->Append(FirstLine); fclose(stream); } } } } 151
  19. Hình 98-Hình minh họa hộp thoại mở tập tin - Hộp thoại Font: Hộp thoại Font cho phép chúng ta chọn Font chữ, kích cỡ, kiểu chữ. Các thuộc tính quan trọng hộp thoại Font: Thuộc tính Ý nghĩa Charset Chỉ định tập ký tự Color Màu của font chữ Handle Thẻ quản của hộp thoại Height Chiều cao của font chữ. Chúng ta có thể tính toán sử dụng công thức này: Font.Height = -Font.Size * Font.PixelsPerInch / 72 Name Tên của font chữ Pitch Chỉ định độ rộng của các ký tự có kích thước giống nhau hay không? PixelsPerInch Số điểm trên một inch. Nếu thay đổi sẽ ảnh hưởng đến thuộc tính Height và Size Size Kích thước font chữ Style Kiểu dáng font chữ 152
  20. Đây là ví dụ về việc cài đặt thuộc tính FontStyle của Font chữ của Label thành fsBold và fsUnderline: void __fastcall TForm1::Button1Click(TObject *Sender) { Label1->Font->Style = TFontStyles() Font->Style = TFontStyles(); } Hình 99-Hình minh họa hộp thoại font chữ - Hộp thoại Color: Hộp thoại này cho phép chọn màu chữ. Các thuộc tính thông dụng được liệt kê sau đây: Thuộc tính Ý nghĩa Color Trả về giá trị màu được chọn có kiểu TColor CustomColors Chứa đựng các giá trị màu tùy chọn ở dạng giá trị cơ số 16. Options cdFullOpen: Hiển thị các màu tùy chọn khi hộp thoại được mở ra. cdPreventFullOpen: Hủy các nút định nghĩa màu mới trong hộp thoại, người sử dụng không thể định nghĩa các màu mới. 153
  21. cdShowHelp: Thêm nút Help vào hộp thoại. cdSolidColor: Trực tiếp Windows sử dụng một màu đặt (solid color) gần nhất so với màu được chọn. cdAnyColor: Cho phép người sử dụng chọn các màu không đặt (non-solid color), những màu có thể hiển thị tương xứng bởi sự phối màu. Đoạn mã lệnh sau của sự kiện OnClick trên nút Button1 để mở hộp thoại màu và đổi màu của hình vẽ Shape1. void __fastcall TForm1::Button1Click(TObject *Sender) { if (ColorDialog1->Execute()) Shape1->Brush->Color = ColorDialog1->Color; } Hình 100-Hình minh họa hộp thoại font chữ - Hộp thoại Printer: Hộp thoại này cho phép chọn máy in để có thể từ đó in dữ liệu ra máy in. Thuộc tính quan trọng Printer được liệt kê trong bảng sau: Thuộc tính Ý nghĩa Collate Trả về giá trị cho biết nút đánh dấu Collate có được đánh dấu hay không? Copies Trả về số lượng bản in FromPage Chỉ định trang bắt đầu 154
  22. MaxPage Trả về số lượng trang lớn nhất các trang sẽ in MinPage Trả về số lượng trang nhỏ nhất các trang sẽ in Options Các tùy chọn khi in, là tập các giá trị sau: poDisablePrintToFile: Hủy bỏ nút đánh dấu Print To File. poHelp Displays: Hiển thị nút Help trong hộp thoại. poPageNums: Cho phép nút radio Pages được sử dụng để chỉ định vùng vùng trang in. poPrintToFile: Hiển thị nút đánh dấu Print To File trong hộp thoại. poSelection: Cho phép nút chọn Selection cho phép người sử dụng in văn bản được chọn. poWarning: Phát sinh một thông báo cảnh báo nếu người sử dụng cố gắng gởi một lệnh để gõ bỏ máy in. PrintRange Là kiểu liệt kê cho phép chọn vùng trang in: prAllPages: Nút radio All được chọn. prSelection: Nút radio Selection được chọn. prPageNums: Nút radio Pages được chọn. PrintToFile Nút đánh dấu Print To File được chọn hay không. ToPage Chỉ định trang in kết thúc Một số ví dụ thể hiện một số áp dụng điều chỉnh và sử dụng hộp thoại PrinterDialog. Ví dụ này sử dụng một nút nhấn, một Page control và một hộp thoại Print trên một form. Khi người sử dụng nhấn nút, hộp thoại printer sẽ hiển thị. Người sử dụng có thể chọn bất kỳ tập con các trang để in nội dung trên Page Control (Đối tượng này nằm trong ngăn Win32). Để thực hiện được ví dụ này, chúng ta phải khai báo thêm thư viện : void __fastcall TForm1::Button1Click(TObject *Sender) { PrintDialog1->Options.Clear(); //hiển thị Page Number và Selection PrintDialog1->Options FromPage = 1; //số lượng trang ít nhất là 1 PrintDialog1->MinPage = 1; //tổng số lượng trang in là trang cuối 155
  23. PrintDialog1->ToPage = PageControl1->PageCount; //số lượng trang lớn nhất là tổng số trang. PrintDialog1->MaxPage = PageControl1->PageCount; if (PrintDialog1->Execute()) { int Start, Stop; // kiểm tra xem vùng mà người sử dụng muốn int switch (PrintDialog1->PrintRange) { case prSelection: Start = PageControl1->ActivePage->PageIndex; Stop = Start; break; case prPageNums: Start = PrintDialog1->FromPage - 1; Stop = PrintDialog1->ToPage - 1; break; default: // prAllPages Start = PrintDialog1->MinPage - 1; Stop = PrintDialog1->MaxPage - 1; break; } // bây giờ, in các trang Printer()->BeginDoc(); for (int i = Start; i Pages[i]->PaintTo(Printer()->Handle, 10, 10); if (i != Stop) Printer()->NewPage(); } Printer()->EndDoc(); } } 156
  24. Hình 101-Hình minh họa hộp thoại font chữ - Hộp thoại tìm kiếm: Thuộc tính Ý nghĩa FindText Văn bản cần tìm kiếm Options Các tùy chọn tìm kiếm. Có thể tra cứu thêm trong tài liệu hướng dẫn. Position Vị trí hiển thị hộp thoại Ví dụ sau đây yêu cầu một TRichEdit, một nút nhấn và một hộp thoại TFindDialog. Nhấn lên nút nhấn sẽ hiển thị một hộp thoại tìm kiếm trên góc phải của rich edit. Điền văn bản tìm kiếm vào ô Find what và nhấn nút Find Next sẽ chọn chuỗi trung đầu tiên trong Rich Edit. Trong ví dụ này, chúng ta có sử dụng sự kiện OnFind: void __fastcall TForm1::Button1Click(TObject *Sender) { FindDialog1->Position = Point(RichEdit1->Left + RichEdit1->Width, RichEdit1- >Top); FindDialog1->Execute(); } void __fastcall TForm1::FindDialog1Find(TObject *Sender) { int FoundAt, StartPos, ToEnd; // begin the search after the current selection 157
  25. // if there is one // otherwise, begin at the start of the text if (RichEdit1->SelLength) StartPos = RichEdit1->SelStart + RichEdit1->SelLength; else StartPos = 0; // ToEnd is the length from StartPos // to the end of the text in the rich edit control ToEnd = RichEdit1->Text.Length() - StartPos; FoundAt = RichEdit1->FindText(FindDialog1->FindText, StartPos, ToEnd, TSearchTypes() SetFocus(); RichEdit1->SelStart = FoundAt; RichEdit1->SelLength = FindDialog1->FindText.Length(); } } Hình 102-Hình minh họa tìm kiếm - Hộp thoại ReplaceDialog: Trong hộp thoại này, chúng ta được gặp thêm thuộc tính ReplaceText là văn bản thay thế. Ngoài ra còn bổ sung thêm sự kiện OnReplace để thay thế văn bản. Sau đây là ví dụ về việc sử dụng sự kiện OnReplace: Đoạn mã lệnh sau tìm kiếm một đối tượng TMemo gọi là Memo1 và thay thế văn bản FindText với ReplaceText. Nó sử dụng thuộc tính SelStart, SelLength và SelText. void __fastcall TForm1::ReplaceDialog1Replace(TObject *Sender) { TReplaceDialog *dlg = (TReplaceDialog *)Sender; /* perform a global case-sensitive search for FindText in Memo1 */ int SelPos = Memo1->Lines->Text.Pos(dlg->FindText); if (SelPos > 0) { 158
  26. Memo1->SelStart = SelPos - 1; Memo1->SelLength = dlg->FindText.Length(); // Replace selected text with ReplaceText Memo1->SelText = dlg->ReplaceText; } else MessageBeep(0); } Hình 103-Hình minh họa tìm kiếm và thay thế Hộp thoại PrinterSetupDialog chúng ta có thể nghiên cứu thêm trong tài liệu hướng dẫn. 6.4. Sử dụng đa biểu mẫu Trong nội dung này, chúng ta sẽ nghiên cứu phương pháp sử dụng đa biểu mẫu trong ứng dụng. Đầu tiên, chúng ta đề cập đến thuộc tính FormStyle và trải dài qua suốt nội dung phần này các các ví dụ thực hiện theo từng bước. Thuộc tính FormStyle cho phép chúng ta chọn giữa fsNormal (Form bình thường) và các thuộc tính dùng để tạo ứng dụng nhiều tài liệu (MDI-Multiple Documet Interface). Chúng ta có thể đặt giá trị thành fsStayOnTop để Form trở thành Form luôn luôn nằm trên tất cả các Form khác, đây là một loại Form rất đặc biệt mà Windows chỉ cho phép duy nhất một Form được ở trạng thái Stay On Top. Do đó, chúng ta không thể tạo hai form cùng StayOnTop vào một thời điểm. Ví dụ: Tạo ứng dụng MDI theo từng bước. MDI là một loại ứng dụng trong các hệ điều hành cũ. Tuy nhiên, ứng dụng này vẫn mang một tính chất rất hay. Cấu trúc MDI cho phép một ứng dụng có nhiều Form được quản lý qua một Form cha. Thường được quản lý bởi một thực đơn trong ứng dụng. Về mặt kỹ thuật chúng ta có khái niệm sau: - Một cửa sổ chính của ứng dụng giống như một thùng chứa để chứa các Form con. - Một Form đặc biệt gọi là MDI Client, vỏ bọ của toàn bộ vùng hoạt động của ứng dụng. - Có nhiều form con có cùng kiểu hay khác kiểu, mỗi Form sẽ hiển thị trong vùng làm việc của Form. C++ Builder cho phép phát triển các ứng dụng MDI rất dễ dạng, chúng ta phải tạo ít nhất hai form, một form được cài đặt thuộc tính FormStyle là fsMDIForm (Form cha) và một form được cài đặt thuộc tính fsMDIChild (Form con). 159
  27. Khi đó, chúng ta thêm mã lệnh cho chức năng New của thực đơn File như sau (TChildForm là lớp của form con mang tên ChildForm): TChildForm ChildForm; ChildForm=new TChildForm(Application); ChildForm->Show(); Một thành phần quan trọng của ứng dụng MDI là gắn kết một thực đơn cho tất cả các cửa sổ con một cách tự động bằng thuộc tính WindowMenu, ngoài ra chúng ta sử dụng số thứ tự cho cửa sổ: void TMainForm::New1Click(TObject *Sender) TChildForm *ChildForm; { WindowMenu = Window1; Counter++; ChildForm=new TChildForm(Application); ChildForm->Caption = ChildForm->Caption + “ “ + IntToStr(Counter); ChildForm->Show(); } Chúng ta tiến hành xây dựng menu hoàn chỉnh như sau: Đầu tiên là thực đơn Windows có ít nhất ba thành phần chọn với tiêu đề Cascade, Tile và Arrange Icons. Chúng ta có thể sử dụng một số phương thức dựng sẵn của TForm chỉ để phục vụ cho chương trình MDI: - Phương thức Cascade xếp các cửa sổ con theo tầng, cửa sổ này chồng lớp. - Phương thức Tile sắp xếp các cửa sổ không chồng lớp theo chiều ngang hay chiều đứng. - Phương thức ArrangIcons sẽ sắp xếp các cửa sổ con theo kiểu biểu tượng. Chúng ta sử dụng kết hợp với TActionList để thêm các hành động cho từng cửa sổ thông qua thuộc tính Action bằng các sử dụng các hành động dựng sẵn. Chúng ta cũng hiểu một số thuộc tính: - ActiveMDIChild là một thuộc tính chỉ đọc cho phép truy cập form MDI đang được kích hoạt. Thuộc tính này có thể bị thay đổi khi chúng ta dùng các phương thức Next, Previous để di chuyển đến Form con kế tiếp hoặc form con trước đó. - ClientHandle là thuộc tính nắm giữ thẻ quản của cửa sổ con đang được mở trong vùng làm việc của form chính. - MDIChildren là thuộc tính chứa môt mảng các form con. Chúng ta có thể sử dụng thuộc tính MDIChildCount để tính xem có bao nhiêu cửa sổ con. Bây giờ chúng ta tạo ứng dụng MDI theo từng bước sau: - Bước 1: Tạo một dự án mới. - Bước 2: Đặt tên cho Form1 thành MainForm. - Bước 3: Lưu tên tập tin Unit1 thành Frame và tên Project thành ProMDI. - Bước 4: Điều chỉnh và thêm các thành phần như trong cửa sổ sau: 160
  28. Đối tượng Thuộc tính Giá trị TMainMenu Các mục con của menu File &New Circle New &Bouncing Square Close Clo&se All - &Exit Menu Các mục con của menu Window gắn WindowArrange1 Windows kết với hành động tạo trong ActionList1 WindowCascade1 thông qua các thuộc tính Action như sau: WindowClose1 WindowMinimizeAll1 WindowTileHorizontal1 WindowTileVertical1 Menu Tạo thêm mục con cho menu Windows Count Windows bằng cách thêm một mục chọn MainForm FormStyle fsMDIForm ActionList1 Tạo các sự kiện chuẩn của Windows. Đồng thời, chúng ta thêm một đối tượng Image để đạt được hình vẽ như sau: Hình 104- Thiết kế cửa sổ chính Bước 5: Thêm một Form mới, cài đặt thuộc tính Name thành CircleChildForm, thuộc tính FormStyle thành fsMDIChild, thuộc tính Color thành màu clTeal, Caption thành MDI Child. Lưu tập tin với tên Child đồng thời thêm một Menu và một ColorDialog để đạt được cửa sổ như sau: 161
  29. Hình 105- Form con Circle Bước 6: Khai báo thêm các biến toàn cục như sau: int XCenter, YCenter; int BorderSize; TColor BorderColor, FillColor; Bước 7: Viết mã lệnh cho các chức năng trên Menu như sau: - Fill Color: void __fastcall TCircleChildForm::FillColor1Click(TObject *Sender) { ColorDialog1->Color = FillColor; if(ColorDialog1->Execute() ) { FillColor = ColorDialog1->Color; this->Repaint(); } } - Border Color: void __fastcall TCircleChildForm::BorderColor1Click(TObject *Sender) { ColorDialog1->Color = BorderColor; if(ColorDialog1->Execute() ) { BorderColor = ColorDialog1->Color; this->Repaint(); } } - BorderSize: void __fastcall TCircleChildForm::BorderSize1Click(TObject *Sender) { 162
  30. AnsiString InputString; InputString = IntToStr (BorderSize); if(InputQuery ("Border", "Insert width", InputString) ) { BorderSize = StrToIntDef (InputString, BorderSize); Repaint(); } } - GetPosition: MessageDlg ("The center of the circle is in the position (" + IntToStr (XCenter) + ", " + IntToStr (YCenter) + ").", mtInformation, TMsgDlgButtons() Pen->Width = BorderSize; Canvas->Pen->Color = BorderColor; Canvas->Brush->Color = FillColor; //Vẽ hình Ellipse tại vị trí nhấn chuột Canvas->Ellipse (XCenter-30, YCenter-30, XCenter+30, YCenter+30); 163
  31. } - Sự kiện OnClose của Form: void __fastcall TCircleChildForm::FormClose(TObject *Sender, TCloseAction &Action) { Action = caFree; } Bước 6: Thêm một Form mới, cài đặt thuộc tính Name thành BounceChildForm, thuộc tính FormStyle thành fsMDIChild, thuộc tính Color thành màu clTeal, Caption thành Bouncing Square. Lưu tập tin với tên Child2 đồng thời thêm một Menu, một Timer (ngăn Win32) với thuộc tính Interval là 200, một Shape và một ColorDialog để đạt được cửa sổ như sau: Hình 106-Thiết kế cửa sổ con thứ 2 Viết mã lệnh cho các sự kiện của các đối tượng trên form như sau: - Khai báo để đạt được đoạn mã lệnh của tập tin .h như sau: #ifndef child2H #define child2H // #include #include #include #include #include #include #include enum directions {up_right, down_right, down_left, up_left}; // class TBounceChildForm : public TForm { __published: // IDE-managed Components 164
  32. TShape *Shape1; TTimer *Timer1; TMainMenu *MainMenu1; TMenuItem *Square1; TMenuItem *FillColor1; TMenuItem *N1; TMenuItem *GetPosition1; TMenuItem *Movement1; TMenuItem *Start1; TMenuItem *Stop1; TColorDialog *ColorDialog1; void __fastcall FillColor1Click(TObject *Sender); void __fastcall GetPosition1Click(TObject *Sender); void __fastcall Start1Click(TObject *Sender); void __fastcall Stop1Click(TObject *Sender); void __fastcall Timer1Timer(TObject *Sender); private: // User declarations directions dir; public: // User declarations __fastcall TBounceChildForm(TComponent* Owner); }; - Lệnh FillColor: void __fastcall TBounceChildForm::FillColor1Click(TObject *Sender) { if(ColorDialog1->Execute() ) Shape1->Brush->Color = ColorDialog1->Color; } - Lệnh Get Position: void __fastcall TBounceChildForm::GetPosition1Click(TObject *Sender) { if(ColorDialog1->Execute() ) Shape1->Brush->Color = ColorDialog1->Color; MessageDlg ("The top-left corner of the square was in the position (" + IntToStr (Shape1->Left) + ", " + IntToStr (Shape1->Top) + ")->", mtInformation, TMsgDlgButtons() << mbOK, 0); } - Lệnh Start: 165
  33. void __fastcall TBounceChildForm::Start1Click(TObject *Sender) { Timer1->Enabled = true; Start1->Enabled = false; Stop1->Enabled = true; } - Lệnh Stop: void __fastcall TBounceChildForm::Stop1Click(TObject *Sender) { Timer1->Enabled = false; Start1->Enabled = true; Stop1->Enabled = false; } - Sự kiện OnTimer của Timer1: void __fastcall TBounceChildForm::Timer1Timer(TObject *Sender) { switch(dir){ case up_right: { Shape1->Left = Shape1->Left + 3; Shape1->Top = Shape1->Top - 3; if(Shape1->Top Left + Shape1->Width >= ClientWidth ) dir = up_left; break; } case down_right: { Shape1->Left = Shape1->Left + 3; Shape1->Top = Shape1->Top + 3; if(Shape1->Top + Shape1->Height >= ClientHeight ) dir = up_right; if(Shape1->Left + Shape1->Width >= ClientWidth ) dir = down_left; break; } 166
  34. case down_left: { Shape1->Left = Shape1->Left - 3; Shape1->Top = Shape1->Top + 3; if(Shape1->Top + Shape1->Height >= ClientHeight ) dir = up_left; if(Shape1->Left Left = Shape1->Left - 3; Shape1->Top = Shape1->Top - 3; if(Shape1->Top Left Color = Shape1->Brush->Color; dir = down_left; } - Mã lệnh cho sự kiện OnClose của Form: void __fastcall TBounceChildForm::FormClose(TObject *Sender, TCloseAction &Action) { Action = caFree; } Bước 7: Thêm các khai báo thư viện để Form chính (MainForm) nhận ra các form con bằng cách mở Form chính và chọn File/Include Unit Hdr , chọn Child và Child2, nhấn OK. Bước 8: Viết mã lệnh cho các sự kiện của các đối tượng và sự kiện trên Form chính. 167
  35. - Khai báo các biến thành viên của MainForm như sau: int Count; TCanvas *OutCanvas; FARPROC OldWinProc; void *NewWinProc; Khi đó tập tin thư viện của Form sẽ là: #ifndef FrameH #define FrameH // #include #include #include #include #include #include #include #include #include // class TMainForm : public TForm { __published: // IDE-managed Components TActionList *ActionList1; TWindowArrange *WindowArrange1; TWindowCascade *WindowCascade1; TWindowClose *WindowClose1; TWindowMinimizeAll *WindowMinimizeAll1; TWindowTileHorizontal *WindowTileHorizontal1; TWindowTileVertical *WindowTileVertical1; TMainMenu *MainMenu1; TMenuItem *File1; TMenuItem *New1; TMenuItem *New2; TMenuItem *Close1; TMenuItem *CloseAll1; TMenuItem *N1; TMenuItem *Exit1; 168
  36. TMenuItem *Window1; TMenuItem *Cascade1; TMenuItem *Tile1; TMenuItem *Tile2; TMenuItem *ArrangeIcons1; TMenuItem *MinimizeAll1; TMenuItem *N2; TMenuItem *Count1; TImage *Image1; private: // User declarations int Count; TCanvas *OutCanvas; FARPROC OldWinProc; void *NewWinProc; public: // User declarations __fastcall TMainForm(TComponent* Owner); }; // extern PACKAGE TMainForm *MainForm; // #endif - Thêm phương thức: NewWinProcedure để quản lý mỗi khi nền của Form được vẽ lại sẽ hiển thị và vẽ hình trong Image1 làm hình nền: void TMainForm::NewWinProcedure(TMessage & Msg) { //TODO: Add your source code here int BmpWidth, BmpHeight; int I, J; //gọi hàm xử lý mặc định Msg.Result = CallWindowProc(OldWinProc,ClientHandle, Msg.Msg , Msg.WParam,Msg.LParam); // handle background repaint if(Msg.Msg == WM_ERASEBKGND) { BmpWidth = MainForm->Image1->Width; BmpHeight = MainForm->Image1->Height; 169
  37. if((BmpWidth != 0) && (BmpHeight != 0) ) { //vẽ ảnh làm nền OutCanvas->Handle = (void *) Msg.WParam; for( I = 0; I ClientWidth / BmpWidth; I++) for( J = 0; J ClientHeight / BmpHeight; J ++) OutCanvas->Draw (I * BmpWidth, J * BmpHeight, MainForm->Image1- >Picture->Graphic); } } } - Viết mã lệnh cho sự kiện OnCreate của Form: void __fastcall TMainForm::FormCreate(TObject *Sender) { //tạo cài đặt mới cho hàm vẽ hình nền NewWinProc = MakeObjectInstance (NewWinProcedure); //lưu trữ lại hàm xử lý cũ OldWinProc = (FARPROC) SetWindowLong ( ClientHandle, GWL_WNDPROC, (long) NewWinProc); OutCanvas = new TCanvas(); } - Viết mã lệnh cho lệnh New Cirlce: void __fastcall TMainForm::New1Click(TObject *Sender) { TCircleChildForm *ChildForm; Count++; ChildForm = new TCircleChildForm(Application); ChildForm->Caption = ChildForm->Caption + " " + IntToStr (Count); ChildForm->Show(); } - Viết mã lệnh cho lệnh New Bouncing Square: void __fastcall TMainForm::New2Click(TObject *Sender) { TBounceChildForm *ChildForm; Count++; ChildForm = new TBounceChildForm(Application); ChildForm->Caption = ChildForm->Caption + " " + IntToStr (Count); 170
  38. ChildForm->Show(); } - Lệnh Close gắn kết với hành động WindowClose1. - Viết mã lệnh cho lệnh Close All: void __fastcall TMainForm::CloseAll1Click(TObject *Sender) { int I; for( I = 0; I MDIChildren[I]->Close(); } - Viết mã lệnh cho lệnh Exit: void __fastcall TMainForm::Exit1Click(TObject *Sender) { this->Close(); //đóng Form chính //hoặc có thể sử dụng lệnh đóng ứng dụng: Application->Terminate(); } - Viết mã lệnh cho lệnh Count: void __fastcall TMainForm::Count1Click(TObject *Sender) { int NBounce, NCircle, I; AnsiString ClassName; NBounce = 0; NCircle = 0; for( I = 0; I ClassName()); if(ClassName=="TBounceChildForm" ) NBounce++; else NCircle++; } MessageDlg ("There are " + IntToStr(MDIChildCount) + " child forms." + IntToStr(NCircle)+ " are Circle child windows and "+ IntToStr(NBounce) +" are Bouncing child windows", mtInformation, TMsgDlgButtons() << mbOK, 0); } - Các chức năng khác trong menu window được gắn kết với hành động tương ứng. - Chọn lệnh Project/Options và điều chỉnh ở ngăn Form để có cửa sổ như sau: 171
  39. Hình 107- Điều tác các Form Nhấn nút Ok để lưu lại các thay đổi. Việc làm này khai báo cho hệ thống rằng MainForm sẽ được tạo ra tự động còn hai Form CircleChildForm và BounceChildForm sẽ được tạo ra bằng mã lệnh. Một thuộc tính quan trọng khác của một form đó là thuộc tính BorderStyle. Thuộc tính này định dạng kiểu hiển thị của Form. Chúng ta sẽ hiểu các mà thuộc tính này tác động lên Form như trong ví dụ sau: Hình 108- Kết quả chương trình FormStyle Chương trình trên khi chúng ta nhấn nút New Form sẽ áp dụng kiểu Form sẽ được tạo ra đã chọn ở nhóm ô chọn. Chúng ta sẽ tạo chương trình với các tên sau: 172
  40. Bước 1: Điều chỉnh các thuộc tính cho Form chính theo bảng và hình minh họa sau: Hình 109- Cửa sổ chính Các đối tượng được liệt kê như sau: Đối tượng Thuộc tính Giá trị TForm Caption Borders Name BorderRadioGroup None Single Sizeable RadioGroup Items Dialog Tool Window Sizeable Tool Window ItemIndex 2 Name BtnNewForm Button Caption New Form Bước 2: Tạo Form hai với tên Form2 và để nguyên Form trống. Bước 3: Lưu lại tập tin của hai hai Form: Form1 với tên First, Form2 với Second. Bước 4: Từ cửa sổ thiết kế của Form1, chúng ta chọn File/Include Unit Hdr để khai báo thêm thư viện của Form2 (tức là thư viện Second.h). Bước 5: Viết mã lệnh cho sự kiện OnClick của nút BtnNewForm: void __fastcall TForm1::BtnNewFormClick(TObject *Sender) { TForm2 *NewForm; NewForm = new TForm2(Application); NewForm->BorderStyle = TFormBorderStyle (BorderRadioGroup->ItemIndex); 173
  41. NewForm->Caption = BorderRadioGroup->Items->Strings[BorderRadioGroup- >ItemIndex]; NewForm->Show(); } Bước 6: Nhấn phím F9 để chạy chương trình. Một thuộc tính quan trọng khác của Form là BorderIcons, thuộc tính này cho phép hiển thị một biểu tượng trên tiêu đề của Form để hiển thị các nút nhấn theo các chức năng của menu hệ thống, chúng ta có thể gán các giá trị biSytemMenu, biMinimize, biMaximize, biHelp. Chương trình ví dụ sau cho thấy chức năng của thuộc tính này như sau: Hình 110- Thiết kế chương trình BorderIcons Bước 1: Thiết kế Form giống như hình minh hoạ. Bước 2: Thêm phương thức SetIcons như hình minh hoạ và mã nguồn sau: Hình 111- Thêm phương thức SetIcons Mã lệnh: void __fastcall TForm1::SetIcons(TObject * Sender) { //TODO: Add your source code here 174
  42. AnsiString ClassName; TMenuItem *item; TBorderIcons BorIco= TBorderIcons(); item = (TMenuItem *)Sender; item->Checked = ! item->Checked; if(SystemMenu1->Checked ) BorIco = TBorderIcons() Checked) BorIco = BorIco Checked ) BorIco = BorIco Checked ) BorIco = BorIco Checked = true; MinimizeBox1->Checked = false; MaximizeBox1->Checked = false; Help1->Checked = true; } - Bước 4: Gắn kết phương thức SetIcons cho sự kiện OnClick của các phần tử lệnh của Menu. - Bước 5: Chạy chương trình chúng ta sẽ thấy kiểu dáng Form sẽ bị tác động như thế nào khi chúng ta cho ẩn hiện các nút trên thanh tiêu đề như đóng, thu nhỏ, . Để kết thúc nội dung phần, chúng ta sẽ tạo một chương trình với màn hình Splash. Đây là một kỹ thuật mà các chương trình thường hay sử dụng để hiển thị một màn hình đầu tiên trước khi form chính được hiển thị. Chương trình sau sử dụng một Form chính kiểm tra và in các giá trị là số nguyên tố từ 2 đến 100000, đoạn mã lệnh này xử lý rất lâu nhưng chúng ta sẽ tạo một Form dạng About nhằm hiển thị cửa sổ này như cửa sổ cài đặt trước khi thêm hết tất cả các số nguyên tố. Chúng ta tiến hành theo từng bước sau: Bước 1: Thiết kế Form chính với các thành phần mặc định như sau: 175
  43. Hình 112-Thiết kế chương trình Splash Bảng giá trị thuộc tính được liệt kê như sau: Đối tượng Thuộc tính Giá trị Form Caption Prime Numbers Columns 5 Items “” ListBox akLeft=true akRight=true Anchors akTop=true akBotton=true Menu chính của Form chỉ có hai hai mục chọn: File/Exit và Help/About. Bước 2: Thêm phương thức xử lý kiểm tra giá trị số nguyên có phải là số nguyên tố hay không. Phương thức này trả về giá trị true hoặc false tương ứng với hàm ý là số nguyên tố và không là số nguyên tố. bool TForm1::isPrime(long n) { //TODO: Add your source code here int i; int k; bool kt=true; k = Ceil(sqrt(n)); for (i=2;i<=k;i++) 176
  44. if (n % i ==0) { kt=false; break; } return kt; } Để sử dụng được câu lệnh Ceil (làm tròn) và hàm sqrt (căn bậc 2) chúng ta phải khai báo thư viện math.hpp và math.h bằng cách nhấn Ctrl+Home, thêm câu lệnh sau: #include #include Bước 3: Chọn File/New/Other , chọn AboutBox trong ngăn Forms. Thêm 1 thanh ProgressBar và Image với hình minh hoạ sau: Hình 113- Thiết kế cửa sổ dùng làm Splash Trong hình minh hoạ trên, Image1 có thuộc tính Align là alClient (điền đầy Form) và Bốn thuộc tính của ProgressBar1 có giá trị min = 2 , Max = 100000, Visible = false, Align = alBottom (điền góc dưới của Form). Bước 3: Thêm phương thức MakeSplash (với dạng public) của AboutBox void TAboutBox::MakeSplash() { //TODO: Add your source code here this->BorderStyle = bsNone; this->OKButton->Visible = false; this->ProgressBar1->Visible =true; Show(); Update(); } Bước 4: Chuyển sang Form1 và khai báo thư viện của C++ Builder bằng cách chọn Include Unit Hdr 177
  45. Bước 5: Viết mã lệnh cho sự kiện OnCreate của Form1 như sau: void __fastcall TForm1::FormCreate(TObject *Sender) { int i; TAboutBox *splash; splash=new TAboutBox(Application); splash->MakeSplash(); for( i = 2; i ProgressBar1->Position =i; if(isPrime (i) ) ListBox1->Items->Add (IntToStr (i)); } delete splash; } Bước 6: Viết mã lệnh cho lệnh File/Exit: void __fastcall TForm1::Exit1Click(TObject *Sender) { this->Close(); } Bước 7: Viết mã lệnh cho lệnh File/About: AboutBox->ShowModal(); Bước 8: Nhấn phím F9 để chạy chương trình. Như vậy, chúng ta đã hiểu phương pháp tạo và quản lý các Form trong chương trình. Ngoài các thuộc tính được đề cập ở trên, chúng ta còn một số thuộc tính chưa được đề cập vì ít khi sử dụng chẳng hạn thuộc tính Alpha là giá trị cho phép chúng ta tạo độ đậm cho Form (nếu AlphaBlendValue=0 và AlphaBlend = true, form sẽ trong suốt). 6.5 Thực hành Bài 1: Viết chương trình sử dụng hộp thoại mở tập tin và liệt kê các tên tập tin trong một ListBox hay ListView. Bài 2: Thêm chức năng tìm kiếm và thay thế vào chương trình TextEditor ở trên. Bài 3: Hãy sử dụng hộp thoại Printer và đoạn mã lệnh ví dụ để in văn bản soạn thảo trong RichEdit. 178
  46. BÀI 7 ĐỒ HOẠ VÀ MULTIMEDIA Mã bài : ITPRG03.7 Giới thiệu : Trong bài học này, chúng ta sẽ nghiên cứu về các phương pháp lập trình với đồ họa và multimedia cũng như xây dựng được các ứng dụng đơn giản. Ngoài ra, chúng ta cũng nghiên cứu phương pháp đưa vào các video clip, audio vào ứng dụng. Mục tiêu thực hiện: Học xong bài này học viên sẽ có khả năng: Mô tả các thuộc tính, các phương thức của đối tượng Canvas. Sử dụng một số thuộc tính, phương thức của Canvas. Sử dụng Clipboard với đồ hoạ. Nạp và lưu trữ các tập tin đồ hoạ. Bổ sung các video clip, audio vào trong ứng dụng. Nội dung chính: 7.1 Tổng quan về lập trình đồ hoạ Trong những chương trên, chúng ta đã từng đặt các thành phần VCL như nút nhấn, nhãn, lên cửa sổ Form để tạo ra ứng dụng. Khi chúng ta đặt một nhãn hay một nút nhấn lên Form, hệ điều hành vẽ lên của sổ của Form các đối tượng này. Theo cách lập trình của Windows, chúng ta sử dụng hệ thống Windows API để vẽ thông qua DC của hệ điều hành. Ví dụ, để vẽ một hình chữ nhật, chúng ta có thể sử dụng các lệnh Windows API bằng đoạn mã lệnh tương tự như sau: void fastcall TForm1::ButtonDrawGDIClick(TObject *Sender) { HDC hDC = GetDC(Handle); Rectangle(hDC,10,10,100,100); ReleaseDC(Handle, hDC); } Trong ví dụ này, chúng ta sử dụng hàm GetDC để lấy DC của cửa sổ, sau đó vẽ hình chữ nhật lên form sau đó giải phóng DC bằng cách ReleaseDC. Trong nội dung của bài học này, chúng ta không nghiên cứu phương pháp lập trình trong Windows API. Để có thể nghiên cứu kỹ hơn về nội dung lập trình Windows API, chúng ta có thể tìm đọc sách Lập trình Windows (Programming Windows) để nghiên cứu kỹ hơn về nội dung lập trình này. Do đó, trong nội dung này chúng ta chỉ nghiên cứu phương pháp lập trình do chính C++ Builder cung cấp, đó là sử dụng đối tượng Canvas sẽ được đề cập trong mục 7.3. 7.2 Các thành phần đồ hoạ VCL 179
  47. VCL cung cấp các đối tượng đồ họa sau. Những đối tượng này có phương thức để vẽ trên canvas, bằng cách sử dụng Canvas vẽ các đối tượng đồ họa và có thể tải và lưu các tập tin đồ họa. Đối tượng Diễn giải Picture Được sử dụng để giữ bất kỳ hình ảnh đồ họa nào. Để thêm một định dạng tập tin mới, chúng ta sử dụng phương thức Picture Register. Sử dụng phương thức này để xử lý độc quyền các tập tin như hiển thị hình ảnh trong một đối tượng hình ảnh. Bitmap Một đối tượng đồ họa mạnh mẽ được sử dụng để tạo, quản lý (co giãn, cuộn, quay và vẽ), lưu trữ các hình ảnh như tập tin trên đĩa. Tạo bản sao của một ảnh bitmap một cách nhanh chóng, đó là bản sao, không phải hình ảnh. Clipboard Đại diện cho côngtenơ cho bất kỳ văn bản hay đồ họa nào mà bị cắt, được sao chép, hay được dán từ hoặc tới một ứng dụng. Với clipboard, bạn có thể có và khôi phục dữ liệu theo khuôn dạng thích hợp; quản lý tham chiếu đếm , và sự mở và đóng Clipboard; quản lý và thao tác những định dạng cho những đối tượng trong clipboard. Icon Ấn định giá trị tải từ một tập tin tin biểu tượng (.ICO file). Metafile Chứa đựng một tập tin bao gồm các phép toán yêu cầu để tạo hình ảnh chứ không phải chứa các điểm ảnh thực của hình ảnh. 7.3 Sử dụng các thuộc tính và các phương thức của đối tượng Canvas Đối tượng Canvas không tìm thấy trên bảng công cụ của C++ Builder. Canvas là một thuộc tính con, gắn liền với hầu hết các đối tượng đồ họa. Canvas đóng vai trò như một tấm vải vẽ, chúng ta có thể sử dụng bút vẽ (pen), màu (color), cọ vẽ (brush), điểm ảnh (pixel), chế độ vẽ (pen mode) cùng các phương thức đơn giản như Line, Ellipse, Circle, để vẽ lên những hình, tử đơn giản đến phức tạp. Đối với Form, Canvas chính là bề mặt của cửa sổ. Đối với ảnh Bitmap, Canvas chính là bề mặt mà các điểm ảnh (pixel) thể hiện trên đó. Ví dụ, để vẽ hình Ellipse lên cửa sổ Form ta gọi lệnh sau: Form1->Canvas->Ellipse(10,10,50,100); Chúng ta cũng có thể sao chép nội dung ảnh vẽ từ một Canvas này sang Canvas khác. Ví dụ dưới đây sẽ tạo ra đối tượng TBitmap, nạp ảnh .bmp từ đĩa từ đĩa vào Canvas của TBitmap, sau đó chép ảnh sang Canvas của Form: void TForm1::Button1Click(TObject *Sender) { TRect r; Graphics::TBitmap *bmp; bmp = new Graphics::TBitmap(); bmp->LoadFromFile("C:\\Windows\\Athen.bmp"); 180
  48. r= Rect(0,0,bmp->Width,bmp->Height); this->Canvas->CopyRect(this->ClientRect,bmp->Canvas,r); delete bmp; } Sau đây sẽ liệt kê các thuộc tính và phương thức của đối tượng Canvas. Bảng thuộc tính thông dụng của đối tượng Canvas. Thuộc Diễn giải tính Font Chỉ định font được sử dụng khi ghi văn bản trên hình ảnh. Tập hợp các thuộc tính của đối tượng TFont chỉ định bề mặt font, màu, kích cỡ và kiểu dáng của font chữ. Brush Chỉ định màu và mẫu của Canvas sử dụng để điền các hình vẽ đồ họa và nền. Tập các thuộc tính của đối tượng TBrush chỉ định màu và mẫu hay hình ảnh sử dụng khi điền trong không gian của Canvas. Pen Chỉ định dạng của bút vẽ sử dụng để vẽ đường và đường viền. PenPos Chỉ định vị trí của bút vẽ. Pixels Chỉ định mà của một vùng của các điểm trong ClipRect hiện tại. Một số phương thức thông dụng của Canvas. Phương thức Diễn giải Arc Vẽ một cung tròn. Chord Một cung tròn khép kín bằng cách nối điểm đầu và điểm cuối của cung tròn. CopyRect Sao chép một phần của bức ảnh từ canvas khác vào canvas hiện tại. Draw Vẽ một đối tượng hình ảnh chỉ định bởi các tham số đồ họa. Ellipse Vẽ hình ellipse định nghĩa bởi một hình chữ nhật làm biên. FillRect Điền một hình chữ nhật chỉ định trên canvas sử dụng cọ vẽ (brush) hiện tại. FloodFill Điềm một vùng của canvas sử dụng cọ vẽ hiện tại. FrameRect Vẽ một hình chữ nhật sử dụng cọ vẽ của canvas để vẽ đường viền. LineTo Vẽ một đường thẳng trên canvas từ PenPos đến vị trí X và Y chỉ định; đưa vị trí bút vẽ đến (X,Y). 181
  49. MoveTo Thay đổi vị trí vẽ hiện tại đến điểm (X,Y). Pie Vẽ một hình bánh là của một hình ellipse được định biên bởi hình chữ nhật (X1,Y1,X2,Y2). Polygon Vẽ một loạt các đường thẳng trên canvas kết nối các điểm và đóng bằng cách vẽ đường thẳng nối điểm đầu với điểm cuối. Polyline Vẽ một loạt các đường thằng trên canvas với bút vẽ hiện tại, nối chúng lại với nhau bằng cách chuyển các điểm vào Points. Rectangle Vẽ một hình chữ nhật trên canvas với đỉnh trên (X1,Y1) và góc dưới (X2,Y2) sử dụng bút vẽ và điền nó sử dụng cọ vẽ. RoundRect Vẽ một hình chữ nhật với góc tròn. StretchDraw Vẽ một hình ảnh trên Canvas so cho nó khít với hình chữ nhật chỉ định. TextHeight, Trả về giá trị chiều cao và chiều rộng của một chuỗi trong font TextWidth hiện tại. TextOut Ghi một chuỗi trên Canvas. TextRect Ghi một chuỗi bên trong một vùng. Để minh họa, chúng ta xem xét một số ví dụ về đối tượng Canvas và các đối tượng liên quan: void __fastcallTForm1::FormPaint(TObject *Sender) { Canvas->MoveTo(0,0); Canvas->LineTo(ClientWidth, ClientHeight); Canvas->MoveTo(0, ClientHeight); Canvas->LineTo(ClientWidth, 0); } - Đoạn lệnh sau đọc một tập tin hình ảnh và sử dụng nó để làm cọ vẽ: Graphics::TBitmap *BrushBmp = new Graphics::TBitmap; try { BrushBmp->LoadFromFile("MyBitmap.bmp"); Form1->Canvas->Brush->Bitmap = BrushBmp; Form1->Canvas->FillRect(Rect(0,0,100,100)); } __finally { 182
  50. Form1->Canvas->Brush->Bitmap = NULL; delete BrushBmp; } - Đoạn lệnh sau thay đổi cọ vẽ bằng cách dùng màu đỏ và tô bằng đường chéo ca rô: void __fastcall TForm1::Button1Click(TObject *Sender) { TCanvas *pCanvas = Image1->Canvas; pCanvas->Brush->Color = clRed; pCanvas->Brush->Style = bsDiagCross; pCanvas->Ellipse(0, 0, Image1->Width, Image1->Height); } - Trong ví dụ sau sẽ dùng một Timer để vẽ các hình chữ nhật ngẫu nhiên với các đường vẽ được sinh ra ngẫu nhiên: int x, y; // void __fastcall TForm1::FormActivate(TObject *Sender) { WindowState = wsMaximized; Timer1->Interval = 50; randomize(); } // void __fastcall TForm1::Timer1Timer(TObject *Sender) { x = random(Screen->Width - 10); y = random(Screen->Height - 10); Canvas->Pen->Color = (Graphics::TColor) random(65535); switch (random(5)) { case 0: Canvas->Pen->Style = psSolid; break; case 1: Canvas->Pen->Style = psDash; break; case 2: Canvas->Pen->Style = psDot; break; case 3: Canvas->Pen->Style = psDashDot; break; case 4: Canvas->Pen->Style = psDashDotDot; break; } Canvas->Rectangle(x, y, x + random(400), y + random(400)); 183
  51. } - Dùng Canvas để vẽ một dòng văn bản lên bề mặt form: void __fastcall TForm1::Button1Click(TObject *Sender) { Canvas->Pen->Color = clBlue; Canvas->MoveTo( 10, 10 ); Canvas->LineTo( 100, 100 ); Canvas->Brush->Color = clBtnFace; Canvas->Font->Name = "Arial"; Canvas->TextOut( Canvas->PenPos.x, Canvas->PenPos.y,"This is the end of the line" ); } 7.4. Tạo và quản lý các bức vẽ Trong nội dung phần này, chúng ta sẽ vận dụng các kiến thức đã học để tiến hành tạo các ứng dụng bằng cách sử dụng Canvas và các công cụ hỗ trợ khác. Chương trình đầu tiên là chương trình Dooble trong bộ ví dụ kèm theo của C++ Builder 6. Đầu tiên chúng ta tạo một dự án với giao diện sau: -Form đầu tiên: Là form sẽ dùng để vẽ. - Đối tượng Image với tên Image1và thuộc tính Align là alClient. - Đối tượng Form với tên DoodleForm, thuộc tính Caption là Doodle. - Lưu Form này với tên Unit là: main. Hình 114- Cửa sổ dùng làm cửa sổ vẽ - Form thứ hai: Chứa các công cụ dùng để lựa chọn màu và các hình vẽ 184
  52. Hình 115- Cửa sổ lựa chọn màu và hình vẽ Form này có tên TollPalatte và được lưu vào unit Palette. Các đối tượng trong khung bên trái có thể được liệt kê từ trên xuống dưới như sau: - Page Control: Trong ngăn công cụ Win32: + Thuộc tính Style: tsTabs. + Nhấn chuột phải chọn New page, đặt Caption là Color. + Nhấn chuột phải chọn New Page, dặt Caption là Tool - Chọn ngăn Color đặt các đối tượng như hình vẽ và đổi tên sơ đồ mũi tên: + Panel1: Đối tượng Panel từ ngăn công cụ Standard. + BGShape và FGShape: Đối tượng Shape từ ngăn công cụ Addtional và tiến hành điều chỉnh các thuộc tính trong Brush để có màu đen và màu trắng như trên hình. + ColorGrid1: Đối tượng CColorGrid trong ngăn công cụ Samples. - Chọn ngăn Tool để đặt các đối tượng SpeedButton và tiến hành điều chỉnh các hình ảnh (thuộc tính Glyph), thuộc tính Group =1, thuộc tính Down để đạt được kết quả như trên hình trên. Tiến hành viết mã lệnh để đạt được các tập tin như sau: Tập tin main.cpp: #include #pragma hdrstop #include "main.h" #include "palette.h" // #pragma resource "*.dfm" #pragma resource "extrares.res" TDoodleForm *DoodleForm; // __fastcall TDoodleForm::TDoodleForm(TComponent* Owner) 185
  53. : TForm(Owner) { } // void __fastcall TDoodleForm::FormCreate(TObject *Sender) { HINSTANCE HInst; HInst = reinterpret_cast (HInstance); // Load custom cursors for tools from extrares.res Screen->Cursors[crFill] = LoadCursor(HInst, "FILL"); Screen->Cursors[crPlus] = LoadCursor(HInst, "PLUS"); Screen->Cursors[crDraw] = LoadCursor(HInst, "DRAW"); Screen->Cursors[crErase] = LoadCursor(HInst, "ERASE"); Cursor = TCursor(crDraw); } // void __fastcall TDoodleForm::Image1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { // The Doodle form contains an Image control on which all of the painting // will occur. Another option would have been to draw directly on the // form's canvas. The benefit of drawing on the Image control is that // it knows how to repaint itself. if (ToolPalette->FillButton->Down) { // The fill tool is the only one that makes use of the right mouse // button. The right mouse button is used to fill with the BG color. if (Button == mbLeft) Image1->Canvas->Brush->Color = ToolPalette->FGShape->Brush->Color; else Image1->Canvas->Brush->Color = ToolPalette->BGShape->Brush->Color; Image1->Canvas->FloodFill(X, Y, Image1->Canvas->Pixels[X][Y], fsSurface); return; } // Otherwise, only the left mouse button is of interest. if (Button != mbLeft) return; 186
  54. if (ToolPalette->EraseButton->Down) { Image1->Canvas->Pen->Color = ToolPalette->BGShape->Brush->Color; Image1->Canvas->Brush->Color = ToolPalette->BGShape->Brush->Color; Image1->Canvas->Pen->Width = 13; Image1->Canvas->Rectangle(X-1, Y-1, X, Y); Image1->Canvas->MoveTo(X,Y); return; } if (ToolPalette->PencilButton->Down) { Image1->Canvas->Pen->Color = ToolPalette->FGShape->Brush->Color; Image1->Canvas->Brush->Color = ToolPalette->BGShape->Brush->Color; Image1->Canvas->MoveTo(X,Y); return; } // At this point, we know we are dealing with a circle, solid circle, // square, or solid square InitialX = X; InitialY = Y; // TmpImage will hold the image as it existed when the mouse went down. // This will be retained until the mouse is released. This image will be // used to restore the image during sizing of squares and circles. TmpImage = new TImage(this); TmpImage->Picture = Image1->Picture; } // void __fastcall TDoodleForm::Image1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { // Only the left mouse button is of interest if (!Shift.Contains(ssLeft)) return; if (ToolPalette->FillButton->Down) return; if (ToolPalette->PencilButton->Down) 187
  55. { Image1->Canvas->LineTo(X,Y); return; } if (ToolPalette->EraseButton->Down) { Image1->Canvas->LineTo(X,Y); return; } // At this point, we know we are dealing with an ellipse or rectangle, // filled or not filled. DrawShape(X, Y); } // void __fastcall TDoodleForm::Image1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { // Only the left mouse button is of interest if (Button != mbLeft) return; if ((ToolPalette->FillButton->Down) || (ToolPalette->PencilButton->Down)) return; if (ToolPalette->EraseButton->Down) { Image1->Canvas->Pen->Width = 1; return; } // At this point, we know we are dealing with an ellipse or rectangle, // filled or not filled. DrawShape(X, Y); delete TmpImage; } // 188
  56. void __fastcall TDoodleForm::FormActivate(TObject *Sender) { // Contains the code for positioning and showing the tool palette. // The call to ToolPalette->Show() could have gone in DoodleForm's // Show method if not for the desire to position the window. if (ToolPalette->Visible) return; // Initial position of the palette relative to the main form ToolPalette->Top = DoodleForm->Top + (DoodleForm->Height / 6); ToolPalette->Left = DoodleForm->Left - ((DoodleForm->Width / 5)); ToolPalette->Show(); } // void __fastcall TDoodleForm::DrawShape(int X, int Y) { TRect bounds; // for graphics functions which require a rect // Start with the original image that we saved when the button // first went down. This blows away any previous rectangles or ellipses // that were drawn while the user dragged. Image1->Picture = TmpImage->Picture; // The above line blew away the brush. The line below restores it. Image1->Canvas->Brush->Color = ToolPalette->FGShape->Brush->Color; Image1->Canvas->Pen->Color = ToolPalette->FGShape->Brush->Color; // Some graphics functions do not allow a rectangle with its bottom above // its top, or right to the left of its left side. This code is to deal // with the case that the user started dragging from the bottom or right. if (X < InitialX) { bounds.Left = X; bounds.Right = InitialX; } else { bounds.Right = X; bounds.Left = InitialX; } 189
  57. if (Y CircleButton->Down) Image1->Canvas->Arc(InitialX, InitialY, X, Y, X, Y, X, Y); else if (ToolPalette->SolidCirButton->Down) Image1->Canvas->Ellipse(InitialX, InitialY, X, Y); else if (ToolPalette->SquareButton->Down) Image1->Canvas->FrameRect(bounds); else if (ToolPalette->SolidSqButton->Down) Image1->Canvas->FillRect(bounds); } // void __fastcall TDoodleForm::FormShow(TObject *Sender) { SetFocus(); } // void __fastcall TDoodleForm::FormClose(TObject *Sender, TCloseAction &Action) { Application->Terminate(); } // Tập tin palette.cpp: #include #pragma hdrstop #include "palette.h" #include "main.h" 190
  58. // #pragma link "CGRID" #pragma resource "*.dfm" TToolPalette *ToolPalette; // __fastcall TToolPalette::TToolPalette(TComponent* Owner) : TForm(Owner) { } // void __fastcall TToolPalette::ColorGrid1Change(TObject *Sender) { // Set the colors of the glyphs that show foreground and background color. // Since the ColorGrid already displays FG and BG, the glyphs aren't really // needed. Note that in the case of the color being black, the color of // the boarder of the glyph (aka the Pen) is changed to white. This is // just so that the frame can still be seen against the fill color. FGShape->Brush->Color = ColorGrid1->ForegroundColor; if (FGShape->Brush->Color == TColor(clBlack)) FGShape->Pen->Color = TColor(clWhite); else FGShape->Pen->Color = TColor(clBlack); BGShape->Brush->Color = ColorGrid1->BackgroundColor; if (BGShape->Brush->Color == TColor(clBlack)) BGShape->Pen->Color = TColor(clWhite); else BGShape->Pen->Color = TColor(clBlack); } // void __fastcall TToolPalette::ShapeButtonClick(TObject *Sender) { DoodleForm->Cursor = TCursor(crPlus); } // void __fastcall TToolPalette::FillButtonClick(TObject *Sender) { 191
  59. DoodleForm->Cursor = TCursor(crFill); } // void __fastcall TToolPalette::PencilButtonClick(TObject *Sender) { DoodleForm->Cursor = TCursor(crDraw); } // void __fastcall TToolPalette::EraseButtonClick(TObject *Sender) { DoodleForm->Cursor = TCursor(crErase); } // Chương trình thứ 2 phức tạp hơn, một chương trình xử lý trực tiếp trên hình ảnh của của hình vẽ. Bước 1: Đầu tiên, chúng ta thêm vào form bốn đối tượng ColorDialog1, FontDialog1, OpenDialog1, SaveDialog1. Bước 2: Đặt đối tượng Panel1 với thuộc tính Align là alLeft. Tiến hành đặt các đối tượng theo hình mình họa và thuyết minh sau: - Group1: Caption là “Image Informatoin”; Align: alTop - ButtonLoad, ButtonSave với thuộc tính Caption là “Load Image ” và “Save Image ” - Label với Caption File Name: - EditFile: Text = “”. - Hai Label với Caption là Width: và Height: - EditWidth, EditHeight với Text là “0”. - Label với Caption Pixel Format:. - EditPixel với Text là “0”. - CheckBoxFlickerFree với Caption là Flicker Free Drawing - Group2 với Caption là “Color, Pen and Brush Settings”; Align: alTop. - Label với Caption “Pen Color”; EditPenColor với Text là “255”; PanelPenColor với Color là clRed; ButtonColor với Caption là Color - Label với Caption: “Pen Width”;EditPenWidth với Text: “1”. - Label với Caption: BrushColor; EditBrushColor với Text: “65535”; PanelBrushColor với color: clYellow; Button1 với Caption: “Color”. - CheckBoxClearBrush với Caption: Clear Brush (no color) - Group3 với Caption: “Mouse Movement”; Align: alTop. - Label với Caption: “X”; EditX với Text: “0”; Label với Caption “Y”; EditY với Text “0” - Label với Caption:192 “Pixel Value”; EditPixelValue với Text “”
  60. Hình 116- Thiết kế PanelLeft Bước 3: Thêm đối tượng PanelRight với Align: alClient. PageControl1 với cùng 4 Tab với 4 Caption: “Drawing Lines, Curves, and Shapes”, “Adding Text Over Image”, “Tweaking The Image”, “Image Effects”. PanelImage với Align: alClient chứa trong nó đối tượng Image1 với thuộc tính Align: alClient. Hình 117- Thiết kế PanelRight Bước 3: Thiết kế Tab đầu tiên Drawing Lines để vẽ các đường thẳng lên ảnh. 193
  61. - Hai Label với Caption “X”, “Y”. - Label với Caption “Start:”; EditFromX và EditFromY với Text: “0”; ButtonLineTo và ButtonDrawSinceWave với Caption “Draw Line To” và “DrawSineWave”. - Label với Caption “Stop:”; EditToX và EditToY với Text “100”; ButtonRectangle và ButtonDrawHexagon với Caption “Draw Rectangle” và “Draw Hexagon”. Hình 118- Thiết kế Tab Drawing Lines Bước 4: Thiết kế Tab Adding Text Over Image để phục vụ chức năng vẽ chữ lên ảnhs - Hai Label với Caption “X” và “Y”. - Label với Caption “Pos:”; EditTextX và EditTextY với Text “0”; Label với Caption “Font:”; EditFont với Caption “Arial”; ButtonFont với Caption “Font ”. - Label với Caption “Color:”; EditFontColor với Text “8388608”; PanelFontColor với Color: clNavy; Button2 với Caption “Color ”; - Label với Caption “Text”; EditText với Text “”; ButtonApplyText với Caption Apply. 194
  62. Hình 119- Thiết kế Tab Adding Text Over Image Bước 5: Thiết kế Tab Tweaking The Image để phục vụ chức năng cắt xén ảnh. -RadioGroupMouse với Caption “Mouse Image Actions”, Items chứa 3 dòng: (“Free Pen Draw”, “Bezier Poly Draw”, “Crop / Zoom Window”), ItemIndex =0. - ButtonCropToSelection với Caption “Crop Image To Selection”. - ButtonRotate với Caption “Rote Image 90”. - CheckBoxStretchImage với Caption “Stretch Image To Fit”; RadioButtonPixelsArray và RadioButtonScanline với Caption “Use Pixels Array (slower)” và “Use Scanline (faster)”. - ButtonBorder với caption “Draw Border Arround Hình 120- Thiết kế Tab TweakingImage”. The Image Bước 6: Thiết kế tab Image Effects để phục vụ xử lý ảnh - ButtonZoom, ButtonSinusoid, ButtonLowPass, ButtonEdge, ButtonTreshold với Caption “Zoom”, “Sinusoid”, “Edge”, “Threshold”. - ButtonBilinear, buttonSinusoidFast, ButtonInvert,ButtonHIstogram với Caption “Bilinear”, “Sinusoid (Fast)”, “Invert”, “Histogram”. Hình 121- Thiết kế Tab Image Effects 195
  63. Bước 7: Sau khi thiết kế xong Form chúng ta viết lệnh cho form với mã lệnh được liệt kê sau đây (lưu ý tập tin unit: ImageProcessForm: #include #include #include #pragma hdrstop #include "ImageProcessForm.h" // #pragma package(smart_init) #pragma resource "*.dfm" int Bilinear(BYTE Image[][256], float i, float j) { int x1, y1, x2, y2; float Gray; x1 = (int)floor(i); x2 = x1+1; y1 = (int)floor(j); y2 = y1+1; Gray = (y2-j)*(x2-i)*Image[x1][y1] + (y2-j)*(i-x1)*Image[x2][y1] + (j-y1)*(x2-i)*Image[x1][y2] + (j-y1)*(i-x1)*Image[x2][y2]; return INT(Gray); } int Lowpass(BYTE Image[][256], int x, int y) { int Mask[3][3] = {{1, 1, 1,}, {1, 1, 1}, {1, 1, 1}}; int i, j, sum=0; for (j=-1; j<=1; j++) for (i=-1; i<=1; i++) sum += Image[x+i][y+j]*Mask[i+1][j+1]; return INT(sum/9.0); } int Edge(BYTE Image[][256], int x, int y) 196
  64. { int Mask[3][3] = {{-1, -1, -1,}, {-1, 9, -1}, {-1, -1, -1}}; int i, j, sum=0; for (j=-1; j Checked) // TForm:: // WndProc(Msg); //inherited; // Erase the backGround } // void __fastcall TForm1::ButtonLoadClick(TObject *Sender) { 197
  65. //This code requires "jpeg.hpp" to be included in the source file OpenDialog1->Filter = "Bmp files (*.bmp)|*.BMP| JPEG images (*.jpg) | *.jpg; " ; if (OpenDialog1->Execute()) { if (!FileExists(OpenDialog1->FileName)) return; // make sure it exists, else get out. AnsiString temp2 = ExtractFileName(OpenDialog1->FileName); AnsiString temp = ExtractFileExt(OpenDialog1->FileName); AnsiString Ext = temp.LowerCase(); if (Ext.AnsiPos("jpg") > 0) // it's a jpg { // Decompress the jpeg image into a bitmap. TJPEGImage *myjpeg = new TJPEGImage(); myjpeg->LoadFromFile(OpenDialog1->FileName); myjpeg->DIBNeeded(); // used when jpeg image needs bitmap rep Image1->Picture->Bitmap->Assign(myjpeg); delete myjpeg; } else if (Ext.AnsiPos("bmp") > 0) { Image1->Picture->Bitmap->LoadFromFile(OpenDialog1->FileName); } EditFile->Text = ExtractFileName(OpenDialog1->FileName); EditWidth->Text = Image1->Width; EditHeight->Text = Image1->Height; EditPixelFormat->Text = Image1->Picture->Bitmap->PixelFormat; } } // void __fastcall TForm1::ButtonSinusoidClick(TObject *Sender) { int x, y; int Amplitude, Gray; float Period; TColor RGB; Image1->Picture->Bitmap = new Graphics::TBitmap; 198
  66. Image1->Picture->Bitmap->PixelFormat = pf24bit; Image1->Picture->Bitmap->Width = 256; Image1->Picture->Bitmap->Height = 256; for (y=0; y Canvas->Pixels[x][y] = RGB; } } // void __fastcall TForm1::ButtonSinusoidFastClick(TObject *Sender) { int x, y; int Amplitude; float Period; BYTE ImageData[256][256], *LinePtr; Image1->Picture->Bitmap = new Graphics::TBitmap; Image1->Picture->Bitmap->PixelFormat = pf24bit; Image1->Picture->Bitmap->Width = 256; Image1->Picture->Bitmap->Height = 256; for (y=0; y Picture->Bitmap->ScanLine[y]; for (x=0; x<=255; x++) { 199
  67. LinePtr[x*3] = ImageData[x][y]; // Red LinePtr[x*3+1] = ImageData[x][y]; // Green LinePtr[x*3+2] = ImageData[x][y]; // Blue } } Image1->Refresh(); } // void __fastcall TForm1::ButtonInvertClick(TObject *Sender) { int x, y; BYTE* LinePtr; // image inverting for (y=0; y Picture->Bitmap->ScanLine[y]; for (x=0; x Refresh(); } // void __fastcall TForm1::ButtonSaveClick(TObject *Sender) { //This code requires "jpeg.hpp" to be included in the source file // SaveDialog1->Filter = // "Bmp files (*.bmp)|*.BMP| JPEG images (*.jpg; *.jpeg) | *.jpg; *;jpeg" ; SaveDialog1->Title = "Save Image"; SaveDialog1->DefaultExt = "jpg"; // SaveDialog1->Filter = "JPG|*.jpg"; SaveDialog1->Filter = "JPEG images (*.jpg) | *.jpg; | Bmp files (*.bmp)|*.BMP" ; SaveDialog1->FilterIndex = 1; 200
  68. if (SaveDialog1->Execute()) { AnsiString temp2 = ExtractFileName(SaveDialog1->FileName); AnsiString temp = ExtractFileExt(SaveDialog1->FileName); AnsiString Ext = temp.LowerCase(); if (Ext.AnsiPos("jpg") > 0) // it's a jpg { // Decompress the jpeg image into a bitmap. TJPEGImage *jp = new TJPEGImage(); try { jp->Assign(Image1->Picture->Bitmap); jp->SaveToFile(SaveDialog1->FileName); } __finally { delete jp; } } else if (Ext.AnsiPos("bmp") > 0) { Image1->Picture->Bitmap->SaveToFile(SaveDialog1->FileName); } } } // void __fastcall TForm1::ButtonZoomClick(TObject *Sender) { BYTE ImageData[256][256], *LinePtr; BYTE Output[256][256]; int x, y, i, j; int zoom=4, x0=127, y0=127; // Copy the image data to TBitmap for (y=0; y<=255; y++) 201
  69. { LinePtr = (BYTE *) Image1->Picture->Bitmap->ScanLine[y]; for (x=0; x Picture->Bitmap->ScanLine[y]; for (x=0; x Refresh(); } // void __fastcall TForm1::ButtonBilinearClick(TObject *Sender) { BYTE ImageData[256][256], *LinePtr; BYTE Output[256][256]; int x, y; float i, j, zoom=4.0; int x0=127, y0=127; // Copy the image data to TBitmap for (y=0; y Picture->Bitmap->ScanLine[y]; for (x=0; x<=255; x++) 202
  70. ImageData[x][y] = LinePtr[x]; } // Calculate the out image for (y=0; y Picture->Bitmap->ScanLine[y]; for (x=0; x Refresh(); } // void __fastcall TForm1::ButtonLowPassClick(TObject *Sender) { BYTE ImageData[256][256], *LinePtr; BYTE Output[256][256]; int x, y; // Copy the image data to TBitmap for (y=0; y Picture->Bitmap->ScanLine[y]; for (x=0; x<=255; x++) ImageData[x][y] = LinePtr[x]; } // Calculate the low-pass image for (y=1; y<=254; y++) for (x=1; x<=254; x++) Output[x][y] = Lowpass(ImageData, x, y); 203
  71. // copy the image back for display for (y=0; y Picture->Bitmap->ScanLine[y]; for (x=0; x Refresh(); } // void __fastcall TForm1::ButtonEdgeClick(TObject *Sender) { BYTE ImageData[256][256], *LinePtr; BYTE Output[256][256]; int x, y; // Copy the image to ImageData for (y=0; y Picture->Bitmap->ScanLine[y]; for (x=0; x Picture->Bitmap->ScanLine[y]; for (x=0; x Refresh(); } 204
  72. // void __fastcall TForm1::ButtonHIstogramClick(TObject *Sender) { unsigned int Histogram[256], Transform[256]; BYTE ImageData[256][256], *LinePtr; int x, y, i, j, sum; // Copy the image to ImageData for (y=0; y Picture->Bitmap->ScanLine[y]; for (x=0; x<=255; x++) ImageData[x][y] = LinePtr[x]; } // initialize the histogram for (i=0; i<=255; i++) Histogram[i] = 0; // Construct the histogram for (y=0; y<=255; y++) for (x=0; x<=255; x++) Histogram[ImageData[x][y]]++; // Compute the transformation for (i=0; i<=255; i++) { sum = 0; for (j=0; j<=i; j++) sum += Histogram[j]; Transform[i] = INT(255.0*sum/(256*256)); } // Transform the image for (y=0; y<=255; y++) for (x=0; x<=255; x++) ImageData[x][y] = Transform[ImageData[x][y]]; // copy the image back for display for (y=0; y<=255; y++) { 205
  73. LinePtr = (BYTE *) Image1->Picture->Bitmap->ScanLine[y]; for (x=0; x Refresh(); } // void __fastcall TForm1::ButtonThresholdClick(TObject *Sender) { int x, y; BYTE* LinePtr; // image thresholding for (y=0; y Picture->Bitmap->ScanLine[y]; for (x=0; x 128) LinePtr[x] = 255; else LinePtr[x] = 0; } } Image1->Refresh(); } // void __fastcall TForm1::FreeZoomWindow() { if (oX != -1) { TRect MyRect = TRect(oX,oY,lX,lY); Image1->Canvas->Rectangle(MyRect); } 206
  74. oX = -1; oY = -1; lX = -1; lY = -1; } // void __fastcall TForm1::Image1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if (Button == mbLeft) { Image1->Canvas->Pen->Color = TColor(EditPenColor->Text.ToIntDef(0)); Image1->Canvas->Pen->Style = psSolid; // solid line Image1->Canvas->Pen->Width = EditPenWidth->Text.ToIntDef(1); if (RadioGroupMouse->Items->Strings[RadioGroupMouse->ItemIndex] == "Bezier Poly Draw") { points[CurrentPoint].x = X; points[CurrentPoint].y = Y; if ((CurrentPoint == 0) || (BezierSequence == 2)) { if (CurrentPoint > 0) NumCurves++; Image1->Canvas->Ellipse(X-2,Y-2, X+2, Y+2); // temporary } else // control point Image1->Canvas->Ellipse(X-1,Y-1, X+1, Y+1); // temporary BezierSequence++; CurrentPoint++; if (BezierSequence >= 3) BezierSequence = 0; } else if (RadioGroupMouse->Items->Strings[RadioGroupMouse->ItemIndex] == 207
  75. "Free Pen Draw")// watch mouse move { if (CheckBoxFlickerFree->Checked) PanelImage->DoubleBuffered = true; Image1->Canvas->MoveTo(X,Y); MouseDown = true; Image1->Canvas->Pen->Mode = pmCopy; } else if (RadioGroupMouse->Items->Strings[RadioGroupMouse->ItemIndex] == "Crop / Zoom Window")// watch mouse move { Image1->Canvas->Pen->Color = clBlack; Image1->Canvas->Pen->Style = psDash; // dashed line Image1->Canvas->Pen->Width = 1; Image1->Canvas->Pen->Mode = pmNotXor; Image1->Canvas->Brush->Style = bsClear; FreeZoomWindow(); oX = X; oY = Y; lX = X; lY = Y; DrawSelectRegion = true; if (CheckBoxFlickerFree->Checked) PanelImage->DoubleBuffered = true; } EditFromX->Text = X; // this is useful for marking positions to draw EditFromY->Text = Y; } else if (Button == mbRight) { if (RadioGroupMouse->Items->Strings[RadioGroupMouse->ItemIndex] == "Bezier Poly Draw") // release and draw Bezier { Image1->Canvas->MoveTo(points[0].x, points[0].y); int size = ((NumCurves)*3); Image1->Canvas->PolyBezierTo(points, size-1); //BezierSequence-1); BezierSequence = 0; 208
  76. NumCurves = 0; CurrentPoint = 0; } else // mark the position in the Font Edit boxes { EditTextX->Text = X; EditTextY->Text = Y; EditToX->Text = X; EditToY->Text = Y; } } } // void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { EditX->Text = X; EditY->Text = Y; EditPixelValue->Text = Image1->Picture->Bitmap->Canvas->Pixels[X][Y]; if (MouseDown) // follow movement of the mouse { Image1->Canvas->Pen->Color = TColor(EditPenColor->Text.ToIntDef(0)); Image1->Canvas->LineTo(X,Y); // Image1->Canvas->Pixels[X][Y] = clWhite; } else if (DrawSelectRegion) { TRect MyRect = TRect(oX,oY,lX,lY); Image1->Canvas->Rectangle(MyRect); lX = X; lY = Y; MyRect = TRect(oX,oY,lX,lY); Image1->Canvas->Rectangle(MyRect); } } 209
  77. // void __fastcall TForm1::Image1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { // if (DrawSelectRegion) // { // TRect MyRect = TRect(oX,oY,lX,lY); // Image1->Canvas->Rectangle(MyRect); // } DrawSelectRegion = false; MouseDown = false; PanelImage->DoubleBuffered = false; EditToX->Text = X; EditToY->Text = Y; } // void __fastcall TForm1::ButtonBorderClick(TObject *Sender) { // let's draw a border around the image TRect MyRect(0,0,Image1->Width,Image1->Height); Image1->Canvas->Pen->Color = TColor(EditPenColor->Text.ToIntDef(0)); //clBlue; Image1->Canvas->Pen->Style = psSolid; // solid line Image1->Canvas->Pen->Width = EditPenWidth->Text.ToIntDef(1); Image1->Canvas->Brush->Style = bsClear; Image1->Canvas->Rectangle(MyRect); } // void __fastcall TForm1::ButtonColorClick(TObject *Sender) { ColorDialog1->Color = (TColor)EditPenColor->Text.ToIntDef(0); if (ColorDialog1->Execute()) { EditPenColor->Text = ColorDialog1->Color; PanelPenColor->Color = ColorDialog1->Color; 210
  78. } } // void __fastcall TForm1::Button1Click(TObject *Sender) { ColorDialog1->Color = (TColor)EditBrushColor->Text.ToIntDef(0); if (ColorDialog1->Execute()) { EditBrushColor->Text = ColorDialog1->Color; PanelBrushColor->Color = ColorDialog1->Color; } } // void __fastcall TForm1::ButtonLineToClick(TObject *Sender) { Image1->Canvas->Pen->Color = TColor(EditPenColor->Text.ToIntDef(0)); //clBlue; Image1->Canvas->Pen->Style = psSolid; // solid line Image1->Canvas->Pen->Width = EditPenWidth->Text.ToIntDef(1); int x1 = EditFromX->Text.ToIntDef(0); int y1 = EditFromY->Text.ToIntDef(0); int x2 = EditToX->Text.ToIntDef(0); int y2 = EditToY->Text.ToIntDef(0); Image1->Canvas->MoveTo(x1,y1); Image1->Canvas->LineTo(x2,y2); } // void __fastcall TForm1::ButtonRectangleClick(TObject *Sender) { Image1->Canvas->Brush->Color = TColor(EditBrushColor->Text.ToIntDef(0)); //clBlue; if (CheckBoxClearBrush->Checked) Image1->Canvas->Brush->Style = bsClear; // clear rectangle else Image1->Canvas->Brush->Style = bsSolid; Image1->Canvas->Pen->Color = TColor(EditPenColor->Text.ToIntDef(0)); //clBlue; 211
  79. Image1->Canvas->Pen->Style = psSolid; // solid line Image1->Canvas->Pen->Width = EditPenWidth->Text.ToIntDef(1); int x1 = EditFromX->Text.ToIntDef(0); int y1 = EditFromY->Text.ToIntDef(0); int x2 = EditToX->Text.ToIntDef(0); int y2 = EditToY->Text.ToIntDef(0); TRect rect(x1,y1,x2,y2); if (CheckBoxFlickerFree->Checked) PanelImage->DoubleBuffered = true; Image1->Canvas->Rectangle(rect); PanelImage->DoubleBuffered = false; } // void __fastcall TForm1::CheckBoxBezierClick(TObject *Sender) { BezierSequence = 0; NumCurves = 0; CurrentPoint = 0; } // void __fastcall TForm1::ButtonDrawCurveyShapeClick(TObject *Sender) { Image1->Canvas->Pen->Color = TColor(EditPenColor->Text.ToIntDef(0)); //clBlue; Image1->Canvas->Pen->Style = psSolid; // solid line Image1->Canvas->Pen->Width = EditPenWidth->Text.ToIntDef(1); // let's draw a sine wave TPoint point[30]; double pi = 3.1514927; point[0].x = 0; point[0].y = 100; point[1].x = 0; point[1].y = 100; for (int i = 1; i<10; i++) 212
  80. { int j = i*3 -1; point[j].x = i*10; point[j].y = (sin(i*(pi/2))*100) + 100; point[j+1].x = i*10; if (point[j].y > 0) point[j+1].y = 300; else if (point[j].y == 0) point[j+1].y = 0; else // 0) point[j+2].y = 300; else if (point[j].y == 0) point[j+2].y = 0; else // Canvas->MoveTo(point[0].x,point[1].y); Image1->Canvas->PolyBezier(point, 29); //BezierSequence-1); } // void __fastcall TForm1::ButtonDrawHexagonClick(TObject *Sender) { Image1->Canvas->Brush->Color = TColor(EditBrushColor->Text.ToIntDef(0)); if (CheckBoxClearBrush->Checked) Image1->Canvas->Brush->Style = bsClear; // clear shape else Image1->Canvas->Brush->Style = bsSolid; Image1->Canvas->Pen->Style = psSolid; // solid line Image1->Canvas->Pen->Width = EditPenWidth->Text.ToIntDef(1); int startx = EditFromX->Text.ToIntDef(0); int starty = EditFromY->Text.ToIntDef(0); int endx = EditToX->Text.ToIntDef(0); int endy = EditToY->Text.ToIntDef(0); 213
  81. int lengthx = abs(endx - startx); int lengthy = abs(endy - starty); TPoint points[5]; points[0] = Point(startx, starty + (lengthy*0.4)); points[1] = Point(startx + (lengthx*0.5), starty); points[2] = Point(startx + (lengthx), starty + (lengthy*0.4)); points[3] = Point(startx + (lengthx*0.7), starty + lengthy); points[4] = Point(startx + (lengthx*0.3), starty + lengthy); if (CheckBoxFlickerFree->Checked) PanelImage->DoubleBuffered = true; Image1->Canvas->Polygon(points, 4); PanelImage->DoubleBuffered = false; } // void __fastcall TForm1::ButtonDrawSineWaveClick(TObject *Sender) { Image1->Canvas->Pen->Color = TColor(EditPenColor->Text.ToIntDef(0)); //clBlue; Image1->Canvas->Pen->Style = psSolid; // solid line Image1->Canvas->Pen->Width = EditPenWidth->Text.ToIntDef(1); const int maxpts = 19; TPoint pts[maxpts]; double pi_3 = 3.1415926535897932384626433832795 / 3.0; int startx = EditFromX->Text.ToIntDef(0); int starty = EditFromY->Text.ToIntDef(0); for (int i = 0; i Canvas->PolyBezier(EXISTINGARRAY(pts)); /* Show the points as well as the curve */ for (int i = 0; i Canvas->Rectangle(pts[i].x-3,pts[i].y-3,pts[i].x+3,pts[i].y+3); 214
  82. } } // void __fastcall TForm1::ButtonFontClick(TObject *Sender) { FontDialog1->Font->Color = TColor(EditFontColor->Text.ToIntDef(0)); if (FontDialog1->Execute()) { EditFont->Text = FontDialog1->Font->Name; Image1->Canvas->Font->Name = FontDialog1->Font->Name; Image1->Canvas->Font->Style = FontDialog1->Font->Style; Image1->Canvas->Font->Size = FontDialog1->Font->Size; Image1->Canvas->Font->Color = FontDialog1->Font->Color; } } // void __fastcall TForm1::ButtonApplyTextClick(TObject *Sender) { Image1->Canvas->Brush->Color = TColor(EditBrushColor->Text.ToIntDef(0)); //clBlue; if (CheckBoxClearBrush->Checked) Image1->Canvas->Brush->Style = bsClear; // clear rectangle else Image1->Canvas->Brush->Style = bsSolid; Image1->Canvas->Font->Name = EditFont->Text; Image1->Canvas->Font->Color = TColor(EditFontColor->Text.ToIntDef(0)); int x = EditTextX->Text.ToIntDef(0); int y = EditTextY->Text.ToIntDef(0); Image1->Canvas->TextOut(x,y,EditText->Text); } // void __fastcall TForm1::ButtonRotateClick(TObject *Sender) { //create and setup a temporary source bitmap 215
  83. Graphics::TBitmap *source = new Graphics::TBitmap; source->Assign(Image1->Picture->Bitmap); source->PixelFormat = Image1->Picture->Bitmap->PixelFormat; // create and setup a temporary destination bitmap Graphics::TBitmap *dest = new Graphics::TBitmap; dest->Width = source->Height; dest->Height = source->Width; dest->PixelFormat = source->PixelFormat; if (RadioButtonPixelsArray->Checked) //Rotate one pixel at a time { for (int x=0;x Width;x++) for(int y=0;y Height;y++) dest->Canvas->Pixels[y][source->Width-1-x] = source->Canvas->Pixels[x][y]; } else // uses the faster scanline method { RGBTRIPLE* pixels; TColor color; for(int y=0;y Height;y++) { pixels = (RGBTRIPLE*)source->ScanLine[y]; for (int x=0;x Width;x++) dest->Canvas->Pixels[y][source->Width-1-x] = TColor(RGB(pixels[x].rgbtRed, pixels[x].rgbtGreen, pixels[x].rgbtBlue)); } } //now assign destination bitmap back to Image1 & cleanup Image1->Picture->Bitmap = dest; delete dest; delete source; } // void __fastcall TForm1::ButtonCropToSelectionClick(TObject *Sender) 216
  84. { // let's crop if (lX == -1) return; // no zoom window to work with // create and setup a temporary destination bitmap Graphics::TBitmap *dest = new Graphics::TBitmap; dest->Width = abs(lX - oX); dest->Height = abs(lY - oY); TRect OldOne = Rect(oX,oY,lX,lY); TRect NewOne = Rect(0,0,dest->Width, dest->Height); FreeZoomWindow(); //create and setup a temporary source bitmap Graphics::TBitmap *source = new Graphics::TBitmap; source->Assign(Image1->Picture->Bitmap); source->PixelFormat = Image1->Picture->Bitmap->PixelFormat; dest->PixelFormat = source->PixelFormat; dest->Canvas->CopyRect(NewOne,source->Canvas,OldOne); //now assign destination bitmap back to Image1 & cleanup Image1->Picture->Bitmap->FreeImage(); Image1->Picture->Bitmap->Assign(dest); delete dest; delete source; } // void __fastcall TForm1::CheckBoxStretchImageClick(TObject *Sender) { Image1->Stretch = CheckBoxStretchImage->Checked; } // void __fastcall TForm1::Button2Click(TObject *Sender) { ColorDialog1->Color = (TColor)EditFontColor->Text.ToIntDef(0); if (ColorDialog1->Execute()) { EditFontColor->Text = ColorDialog1->Color; PanelFontColor->Color = ColorDialog1->Color; 217
  85. } } // Bước 8: Nhấn phí F9 để kiểm thử chương trình. 7.5 Lập trình multimedia. Trong phần này, chúng ta sẽ nghiên cứu các chức năng cơ bản của lập trình multimedia trong C++ Builder. Đầu tiên, chúng ta nghiên cứu phương pháp đưa vào các đoạn video một cách âm thầm vào ứng dụng của chúng ta. Đối tượng hoạt hình trong C++ Builder cho phép chúng ta thêm các trích đoạn video một cách âm thầm vào trong ứng dụng. Để thêm một trích đoạn từ vào một ứng dụng, theo các bước sau: 1. Nhấn đôi chuột lên biểu tượng animate trên trang Win32 của bảng thành phần. Biểu tượng của điều khiển hoạt hình lên form mà bạn muốn hiển thị trích đoạn video. 2. Sử dụng Object Inspector, sử dụng thuộc tính Name và nhập vào tên mới. Chúng ta sử dụng tên này để gọi đối tượng animate. 3. Thực hiện theo các hành động sau: Sử dụng thuộc tính CommonAVI và chọn một AVI cho phép từ danh sách đổ xuống hoặc sử dụng thuộc tính FileName để chọn tập tin AVI. Chọn tài nguyên của một AVI sử dụng thuộc tính ResName và ResID. Sử dụng ResHandle để chỉ định mô đun chứa chỉ định tài nguyên bởi ResName và ResID. Cái này sẽ tải tập tin AVI vào trong bộ nhớ. Nếu chúng ta muốn hiển thị khung đầu tiên của trích đoạn AVI trên màn hình cho đến khi người chơi sử dụng thuộc tính Active hay phương thức Play, sau đó cài đặt thuộc tính Open thành true. 4. Cài đặt thuộc tính Repetitions là số lần lặp lại khi chơi tập tin AVI này, nếu chỉ định là 0 thì có nghĩa lặp lại cho đến khi sử dụng phương thức Stop. 5. Tạo bất kỳ sự thay đổi nào khác cho các cài đặt của điều khiển animation. Ví dụ, nếu chung ta muốn thay đổi khung hình đầu tiên sẽ hiển thị khi đối tượng hoạt hình được mở, hãy cài đặt giá trị StartFrame thanh giá trị của khung hình. 6. Cài đặt giá trị thuộc tính Active thành true và viết mã lệnh thực thi cho sự kiện nếu cần. Như đã thấy, sáu bước trên giúp chúng ta đưa các Video clip dạng AVI vào ứng dụng của chúng ta. Tiếp theo, chúng ta có thể sử dụng đối tượng TMediaPlayer để đưa âm thanh và video clip vào ứng dụng của chúng ta. Nó mở một thiết bị media và play, stop, pause, record, các âm thanh và video clip sử dụng thiết bị media. Thiết bị media có thể là phần cứng hay phần mềm: Để thêm một âm thanh hay video clip vào một ứng dụng, chúng ta theo các bước sau: 1. Nhấn đôi lên biểu tượng MediaPlayer ở trang System của bảng thành phần. 2. Sử dụng cửa sổ Object Inspector để điều chỉnh tên thuộc tính. 3. Chọn thiết bị từ thuộc tính DeviceType. 4. Nếu thiết bị lưu trữ media của nó trong một tập tin. Hãy chỉ định tập tin bằng cách sử dụng thuộc tính FileName 5. Cài đặt thuộc tính AutoOpen thành true. 218
  86. 6. Cài đặt thuộc tính AutoEnable property thành true. 7. Sử dụng thuộc tính EnabledButtons để cho phép hay không cho phép các nút nhấn hiển thị trên đối tượng. Khi đối tượng hoạt động có thể nhấn lên các nút nhấn hay viết mã lệnh để thực hiện chức năng tương ứng như Play, Pause, Stop, Next, Previous, Để kết thúc nội dung phần này, chúng ta xem qua các bước thực hiện chương trình Mp3 Player Bước 1: Tạo Form như hình sau với các liệt kê về cây đối tượng kèm theo dữ liệu của tập tin khai báo Form Hình 122- Thiết kế chương trình Mp3 Player Tập tin .dfm của Form như sau: object Form1: TForm1 Left = 245 Top = 311 BorderStyle = bsNone Caption = 'MCI MP3 Player Demo' ClientHeight = 105 ClientWidth = 402 Color = 10526880 Font.Charset = DEFAULT_CHARSET 219