Hộp thoại chọn tệp hoặc thư mục
Trong nhóm các hàm làm việc với tệp và thư mục, có một hàm cho phép yêu cầu tương tác tên của một tệp, thư mục hoặc một nhóm tệp từ người dùng để truyền thông tin này vào chương trình MQL. Việc gọi hàm FileSelectDialog
sẽ khiến một cửa sổ tiêu chuẩn của Windows để chọn tệp và thư mục xuất hiện trong terminal.
Vì hộp thoại làm gián đoạn việc thực thi chương trình MQL cho đến khi nó được đóng, việc gọi hàm chỉ được phép trong hai loại chương trình MQL chạy trong các luồng riêng biệt: EA và script (xem Các loại chương trình MQL). Việc sử dụng hàm này bị cấm trong các chỉ báo và dịch vụ: loại trước được thực thi trong luồng giao diện của terminal (và việc dừng chúng sẽ làm đóng băng việc cập nhật biểu đồ của các công cụ tương ứng), trong khi loại sau được thực thi ở chế độ nền và không thể truy cập giao diện người dùng.
Tất cả các phần tử của hệ thống tệp mà hàm làm việc đều nằm trong sandbox, tức là trong thư mục của bản sao terminal hiện tại hoặc tester agent (nếu chương trình chạy trong tester), trong thư mục con MQL5/Files
.
Nếu cờ FSD_COMMON_FOLDER
có trong tham số flags
(xem thêm bên dưới), sandbox chung của tất cả các terminal Users/<user>...MetaQuotes/Terminal/Common/Files
được sử dụng.
Giao diện của hộp thoại phụ thuộc vào hệ điều hành Windows. Một trong những tùy chọn giao diện có thể được hiển thị dưới đây.
Hộp thoại chọn tệp và thư mục của Windows
int FileSelectDialog(const string caption, const string initDir, const string filter, uint flags, string &filenames[], const string defaultName)
Hàm này hiển thị một hộp thoại tiêu chuẩn của Windows để mở hoặc tạo tệp hoặc chọn thư mục. Tiêu đề được chỉ định trong tham số caption
. Nếu giá trị là NULL
, tiêu đề tiêu chuẩn sẽ được sử dụng: "Open" để đọc hoặc "Save as" để ghi tệp, hoặc "Select folder", tùy thuộc vào các cờ trong tham số flags
.
Tham số initDir
cho phép đặt thư mục ban đầu mà hộp thoại sẽ mở. Nếu đặt thành NULL
, nội dung của thư mục MQL5/Files
sẽ được hiển thị. Thư mục này cũng được sử dụng nếu một đường dẫn không tồn tại được chỉ định trong initDir
.
Sử dụng tham số filter
, bạn có thể giới hạn tập hợp các phần mở rộng tệp sẽ được hiển thị trong hộp thoại. Các tệp của các định dạng khác sẽ bị ẩn. NULL
có nghĩa là không có giới hạn.
Định dạng của chuỗi filter
như sau:
"<description 1>|<extension 1>|<description 2>|<extension 2>..."
Bất kỳ chuỗi nào cũng được phép làm description
. Bạn có thể viết bất kỳ bộ lọc nào với các ký tự thay thế *
và ?
mà chúng ta đã thảo luận trong phần Tìm kiếm tệp và thư mục làm extensions
. Ký tự |
là dấu phân cách.
Vì mô tả và phần mở rộng liền kề tạo thành một cặp có liên quan logic, tổng số phần tử trong dòng phải là số chẵn, và số lượng dấu phân cách phải là số lẻ.
Mỗi sự kết hợp giữa mô tả và phần mở rộng tạo ra một lựa chọn riêng trong danh sách thả xuống của hộp thoại. Mô tả được hiển thị cho người dùng và phần mở rộng được sử dụng để lọc.
Ví dụ, "Text documents (*.txt)|*.txt|All files (*.*)|*.*"
, trong đó phần mở rộng đầu tiên "Text documents (*.txt)|*.txt"
sẽ được chọn làm loại tệp mặc định.
Trong tham số flags
, bạn có thể chỉ định một mặt nạ bit xác định các chế độ hoạt động bằng toán tử |
. Các hằng số sau được định nghĩa cho nó:
FSD_WRITE_FILE
— chế độ ghi tệp ("Save as"). Nếu không có cờ này, chế độ đọc ("Open") được sử dụng theo mặc định. Nếu cờ này có mặt, việc nhập một tên mới tùy ý luôn được phép, bất kể cờFSD_FILE_MUST_EXIST
.FSD_SELECT_FOLDER
— chế độ chọn thư mục (chỉ một và chỉ hiện có). Với cờ này, tất cả các cờ khác trừFSD_COMMON_FOLDER
bị bỏ qua hoặc gây lỗi. Bạn không thể yêu cầu tạo thư mục một cách rõ ràng, nhưng có thể tạo thư mục tương tác trong hộp thoại và chọn ngay lập tức.FSD_ALLOW_MULTISELECT
— cho phép chọn nhiều tệp trong chế độ đọc. Cờ này bị bỏ qua nếuFSD_WRITE_FILE
hoặcFSD_SELECT_FOLDER
được chỉ định.FSD_FILE_MUST_EXIST
— các tệp được chọn phải tồn tại. Nếu người dùng cố gắng chỉ định một tên tùy ý, hộp thoại sẽ hiển thị cảnh báo và vẫn mở. Cờ này bị bỏ qua nếu chế độFSD_WRITE_FILE
được chỉ định.FSD_COMMON_FOLDER
— hộp thoại được mở cho sandbox chung của tất cả các terminal khách hàng.
Hàm sẽ điền vào một mảng chuỗi filenames
với các tên của tệp hoặc thư mục được chọn. Nếu mảng là động, kích thước của nó thay đổi để phù hợp với lượng dữ liệu thực tế, cụ thể là mở rộng hoặc cắt ngắn xuống 0 nếu không có gì được chọn. Nếu mảng là cố định, nó phải đủ lớn để chứa dữ liệu dự kiến. Nếu không, sẽ xảy ra lỗi 4007 (ARRAY_RESIZE_ERROR
).
Tham số defaultName
chỉ định tên tệp/thư mục mặc định, sẽ được thay vào trường nhập tương ứng ngay sau khi mở hộp thoại. Nếu tham số là NULL
, trường sẽ trống ban đầu.
Nếu tham số
defaultName
được đặt, thì trong quá trình kiểm tra không trực quan của chương trình MQL, lời gọiFileSelectDialog
sẽ trả về 1 và giá trịdefaultName
sẽ được sao chép vào mảngfilenames
.
Hàm trả về số lượng mục được chọn (0 nếu người dùng không chọn gì), hoặc -1 nếu có lỗi.
Hãy xem xét các ví dụ về cách hàm hoạt động trong script FileSelect.mq5
. Trong hàm OnStart
, chúng ta sẽ lần lượt gọi FileSelectDialog
với các cài đặt khác nhau. Miễn là người dùng chọn một thứ gì đó (không nhấp vào nút "Cancel" trong hộp thoại), bài kiểm tra sẽ tiếp tục đến bước cuối cùng (ngay cả khi hàm thực thi với mã lỗi).
void OnStart()
{
string filenames[]; // mảng động phù hợp với mọi lời gọi
string fixed[1]; // mảng quá nhỏ nếu có hơn 1 tệp
const string filter = // ví dụ bộ lọc
"Text documents (*.txt)|*.txt"
"|Files with short names|????.*"
"|All files (*.*)|*.*";
2
3
4
5
6
7
8
Đầu tiên, chúng ta sẽ yêu cầu người dùng chọn một tệp từ thư mục MQL5Book
. Bạn có thể chọn một tệp hiện có hoặc nhập một tên mới (vì không có cờ FSD_FILE_MUST_EXIST
).
Print("Open a file");
if(PRTF(FileSelectDialog(NULL, "MQL5book", filter,
0, filenames, NULL)) == 0) return; // 1
ArrayPrint(filenames); // "MQL5Book\utf8.txt"
2
3
4
Giả sử thư mục chứa ít nhất 5 tệp từ bộ sách, một trong số đó được chọn ở đây.
Sau đó, chúng ta sẽ thực hiện yêu cầu tương tự ở chế độ "ghi" (với cờ FSD_WRITE_FILE
).
Print("Save as a file");
if(PRTF(FileSelectDialog(NULL, "MQL5book", NULL,
FSD_WRITE_FILE, filenames, NULL)) == 0) return; // 1
ArrayPrint(filenames); // "MQL5Book\newfile"
2
3
4
Ở đây, người dùng cũng có thể chọn cả tệp hiện có và nhập tên mới. Việc kiểm tra xem người dùng có định ghi đè lên tệp hiện có hay không phải do lập trình viên thực hiện (hộp thoại không tạo cảnh báo).
Bây giờ, hãy kiểm tra việc chọn nhiều tệp (FSD_ALLOW_MULTISELECT
) trong một mảng động.
if(PRTF(FileSelectDialog(NULL, "MQL5book", NULL,
FSD_FILE_MUST_EXIST | FSD_ALLOW_MULTISELECT, filenames, NULL)) == 0) return; // 5
ArrayPrint(filenames);
// "MQL5Book\ansi1252.txt" "MQL5Book\unicode1.txt" "MQL5Book\unicode2.txt"
// "MQL5Book\unicode3.txt" "MQL5Book\utf8.txt"
2
3
4
5
Sự hiện diện của cờ FSD_FILE_MUST_EXIST
có nghĩa là hộp thoại sẽ hiển thị cảnh báo và vẫn mở nếu bạn cố gắng nhập một tên mới.
Nếu chúng ta cố gắng chọn nhiều hơn một tệp trong một mảng kích thước cố định theo cách tương tự, chúng ta sẽ nhận được lỗi.
Print("Open multiple files (fixed, choose more than 1 file for error)");
if(PRTF(FileSelectDialog(NULL, "MQL5book", NULL,
FSD_FILE_MUST_EXIST | FSD_ALLOW_MULTISELECT, fixed, NULL)) == 0) return;
// -1 / ARRAY_RESIZE_ERROR(4007)
ArrayPrint(fixed); // null
2
3
4
5
Cuối cùng, hãy kiểm tra các thao tác với thư mục (FSD_SELECT_FOLDER
).
Print("Select a folder");
if(PRTF(FileSelectDialog(NULL, "MQL5book/nonexistent", NULL,
FSD_SELECT_FOLDER, filenames, NULL)) == 0) return; // 1
ArrayPrint(filenames); // "MQL5Book"
2
3
4
Trong trường hợp này, thư mục con không tồn tại nonexistent
được chỉ định làm đường dẫn bắt đầu, vì vậy hộp thoại sẽ mở ở gốc của sandbox MQL5/Files
. Ở đó, chúng ta đã chọn MQL5Book
.
Nếu chúng ta kết hợp một tổ hợp cờ không hợp lệ, chúng ta sẽ nhận được một lỗi khác.
if(PRTF(FileSelectDialog(NULL, "MQL5book", NULL,
FSD_SELECT_FOLDER | FSD_WRITE_FILE, filenames, NULL)) == 0) return;
// -1 / INTERNAL_ERROR(4001)
ArrayPrint(filenames); // "MQL5Book"
}
2
3
4
5
Do lỗi, hàm không sửa đổi mảng được truyền vào, và phần tử cũ MQL5Book
vẫn còn trong đó.
Trong bài kiểm tra này, chúng ta đã cố ý chỉ kiểm tra kết quả cho 0 để thể hiện tất cả các tùy chọn, bất kể có lỗi hay không. Trong một chương trình thực tế, hãy kiểm tra kết quả của hàm có tính đến lỗi, tức là với các điều kiện cho ba kết quả: lựa chọn được thực hiện (>0), không chọn (==0), và lỗi (<0).