Tổng quan về các hàm truy cập thuộc tính đối tượng
Các đối tượng có nhiều loại thuộc tính khác nhau có thể được đọc và thiết lập bằng các hàm ObjectGet
và ObjectSet
. Như chúng ta đã biết, nguyên tắc này đã được áp dụng cho biểu đồ (xem phần Tổng quan về các hàm làm việc với tập hợp đầy đủ các thuộc tính biểu đồ).
Tất cả các hàm như vậy đều nhận ba tham số đầu tiên là định danh biểu đồ, tên đối tượng và định danh thuộc tính, mà phải là một thành viên của một trong các liệt kê ENUM_OBJECT_PROPERTY_INTEGER
, ENUM_OBJECT_PROPERTY_DOUBLE
, hoặc ENUM_OBJECT_PROPERTY_STRING
. Chúng ta sẽ nghiên cứu các thuộc tính cụ thể dần dần trong các phần sau. Bảng tổng hợp đầy đủ của chúng có thể được tìm thấy trong tài liệu MQL5, trên trang về Thuộc tính đối tượng.
Cần lưu ý rằng các định danh thuộc tính trong cả ba liệt kê không giao nhau, điều này cho phép kết hợp việc xử lý chung của chúng vào một mã thống nhất duy nhất. Chúng ta sẽ sử dụng điều này trong các ví dụ.
Một số thuộc tính chỉ có thể đọc và sẽ được đánh dấu "r/o" (chỉ đọc).
Như trong trường hợp của API vẽ đồ, các hàm đọc thuộc tính có dạng ngắn và dạng dài: dạng ngắn trực tiếp trả về giá trị được yêu cầu, còn dạng dài trả về thành công kiểu boolean (true
) hoặc lỗi (false
), và giá trị thực tế được đặt trong tham số cuối cùng được truyền bằng tham chiếu. Việc không có lỗi khi gọi dạng ngắn cần được kiểm tra bằng biến tích hợp _LastError
.
Khi truy cập một số thuộc tính, bạn phải chỉ định một tham số bổ sung (modifier
), được sử dụng để chỉ ra số giá trị hoặc mức nếu thuộc tính có nhiều giá trị. Ví dụ, nếu một đối tượng có nhiều điểm neo, thì modifier cho phép bạn chọn một điểm cụ thể.
Dưới đây là các nguyên mẫu hàm để đọc và ghi thuộc tính kiểu số nguyên. Lưu ý rằng kiểu giá trị trong chúng là long
, cho phép lưu trữ các thuộc tính không chỉ của kiểu int
hoặc long
, mà còn bool
, color
, datetime
, và các liệt kê khác nhau (xem bên dưới).
bool ObjectSetInteger(long chartId, const string name, ENUM_OBJECT_PROPERTY_INTEGER property, long value)
bool ObjectSetInteger(long chartId, const string name, ENUM_OBJECT_PROPERTY_INTEGER property, int modifier, long value)
long ObjectGetInteger(long chartId, const string name, ENUM_OBJECT_PROPERTY_INTEGER property, int modifier = 0)
bool ObjectGetInteger(long chartId, const string name, ENUM_OBJECT_PROPERTY_INTEGER property, int modifier, long &value)
Các hàm cho thuộc tính thực được mô tả tương tự.
bool ObjectSetDouble(long chartId, const string name, ENUM_OBJECT_PROPERTY_DOUBLE property, double value)
bool ObjectSetDouble(long chartId, const string name, ENUM_OBJECT_PROPERTY_DOUBLE property, int modifier, double value)
double ObjectGetDouble(long chartId, const string name, ENUM_OBJECT_PROPERTY_DOUBLE property, int modifier = 0)
bool ObjectGetDouble(long chartId, const string name, ENUM_OBJECT_PROPERTY_DOUBLE property, int modifier, double &value)
Cuối cùng, có bốn hàm tương tự cho chuỗi.
bool ObjectSetString(long chartId, const string name, ENUM_OBJECT_PROPERTY_STRING property, const string value)
bool ObjectSetString(long chartId, const string name, ENUM_OBJECT_PROPERTY_STRING property, int modifier, const string value)
string ObjectGetString(long chartId, const string name, ENUM_OBJECT_PROPERTY_STRING property, int modifier = 0)
bool Object GetString(long chartId, const string name, ENUM_OBJECT_PROPERTY_STRING property, int modifier, string &value)
Để nâng cao hiệu suất, tất cả các hàm thiết lập thuộc tính đối tượng (ObjectSetInteger
, ObjectSetDouble
, và ObjectSetString
) là không đồng bộ và thực chất gửi lệnh đến biểu đồ để sửa đổi đối tượng. Khi thực thi thành công các hàm này, các lệnh được đặt trong hàng đợi sự kiện chung của biểu đồ, được chỉ ra bởi kết quả trả về là true
. Khi xảy ra lỗi, các hàm sẽ trả về false
, và mã lỗi phải được kiểm tra trong biến _LastError
.
Thuộc tính đối tượng được thay đổi với một số độ trễ, trong quá trình xử lý hàng đợi sự kiện biểu đồ. Để buộc cập nhật giao diện và thuộc tính của các đối tượng trên biểu đồ, đặc biệt sau khi thay đổi nhiều đối tượng cùng lúc, hãy sử dụng hàm ChartRedraw
.
Các hàm lấy thuộc tính biểu đồ (ObjectGetInteger
, ObjectGetDouble
, và ObjectGetString
) là đồng bộ, tức là mã gọi đợi kết quả thực thi của chúng. Trong trường hợp này, tất cả các lệnh trong hàng đợi biểu đồ được thực thi để lấy giá trị thực tế của các thuộc tính.
Hãy quay lại ví dụ về script để xóa đối tượng, chính xác hơn là phiên bản mới của nó, ObjectCleanup2.mq5
. Nhớ lại rằng trong hàm CustomDeleteAllObjects
, chúng ta muốn triển khai khả năng chọn đối tượng dựa trên thuộc tính của chúng. Giả sử rằng các thuộc tính này nên là màu sắc và điểm neo. Để lấy chúng, sử dụng hàm ObjectGetInteger
và một cặp phần tử liệt kê ENUM_OBJECT_PROPERTY_INTEGER
: OBJPROP_COLOR
và OBJPROP_ANCHOR
. Chúng ta sẽ xem xét chúng chi tiết sau.
Với thông tin này, mã sẽ được bổ sung với các kiểm tra sau (ở đây, để đơn giản, màu sắc và điểm neo được đưa ra bởi các hằng số clrRed
và ANCHOR_TOP
. Thực tế, chúng ta sẽ cung cấp các biến đầu vào cho chúng).
int CustomDeleteAllObjects(const long chart, const string prefix,
const int window = -1, const int type = -1)
{
int count = 0;
for(int i = ObjectsTotal(chart, window, type) - 1; i >= 0; --i)
{
const string name = ObjectName(chart, i, window, type);
// điều kiện về tên và các thuộc tính bổ sung, như màu sắc và điểm neo
if((StringLen(prefix) == 0 || StringFind(name, prefix) == 0)
&& ObjectGetInteger(0, name, OBJPROP_COLOR) == clrRed
&& ObjectGetInteger(0, name, OBJPROP_ANCHOR) == ANCHOR_TOP)
{
count += ObjectDelete(chart, name);
}
}
return count;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Chú ý đến các dòng có ObjectGetInteger
.
Chúng dài dòng và chứa một số sự lặp lại vì các thuộc tính cụ thể được gắn với các hàm ObjectGet
của các kiểu đã biết. Ngoài ra, khi số lượng điều kiện tăng lên, việc lặp lại ID biểu đồ và tên đối tượng có thể dường như dư thừa.
Để đơn giản hóa việc ghi, hãy chuyển sang công nghệ mà chúng ta đã thử nghiệm trong tệp ChartModeMonitor.mqh
trong phần về Chế độ hiển thị biểu đồ. Ý nghĩa của nó là mô tả một lớp trung gian với các phương thức nạp chồng để đọc và ghi thuộc tính của tất cả các kiểu. Hãy đặt tên tệp tiêu đề mới là ObjectMonitor.mqh
.
Lớp ObjectProxy
sao chép chặt chẽ cấu trúc của lớp ChartModeMonitorInterface
cho biểu đồ. Sự khác biệt chính là sự hiện diện của các phương thức ảo để thiết lập và lấy ID biểu đồ và tên đối tượng.
class ObjectProxy
{
public:
long get(const ENUM_OBJECT_PROPERTY_INTEGER property, const int modifier = 0)
{
return ObjectGetInteger(chart(), name(), property, modifier);
}
double get(const ENUM_OBJECT_PROPERTY_DOUBLE property, const int modifier = 0)
{
return ObjectGetDouble(chart(), name(), property, modifier);
}
string get(const ENUM_OBJECT_PROPERTY_STRING property, const int modifier = 0)
{
return ObjectGetString(chart(), name(), property, modifier);
}
bool set(const ENUM_OBJECT_PROPERTY_INTEGER property, const long value,
const int modifier = 0)
{
return ObjectSetInteger(chart(), name(), property, modifier, value);
}
bool set(const ENUM_OBJECT_PROPERTY_DOUBLE property, const double value,
const int modifier = 0)
{
return ObjectSetDouble(chart(), name(), property, modifier, value);
}
bool set(const ENUM_OBJECT_PROPERTY_STRING property, const string value,
const int modifier = 0)
{
return ObjectSetString(chart(), name(), property, modifier, value);
}
virtual string name() = 0;
virtual void name(const string) { }
virtual long chart() { return 0; }
virtual void chart(const long) { }
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Hãy triển khai các phương thức này trong lớp con (sau này chúng ta sẽ bổ sung hệ thống phân cấp lớp với bộ theo dõi thuộc tính đối tượng, tương tự như bộ theo dõi thuộc tính biểu đồ).
class ObjectSelector: public ObjectProxy
{
protected:
long host; // ID biểu đồ
string id; // ID biểu đồ
public:
ObjectSelector(const string _id, const long _chart = 0): id(_id), host(_chart) { }
virtual string name()
{
return id;
}
virtual void name(const string _id)
{
id = _id;
}
virtual void chart(const long _chart)
{
host = _chart;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Terminal ghi nhớ các cài đặt cuối cùng của một loại đối tượng cụ thể, và nếu chúng được tạo liên tiếp, điều này tương đương với sao chép. Tuy nhiên, các cài đặt thường thay đổi trong quá trình làm việc với các biểu đồ khác nhau, và nếu sau một thời gian có nhu cầu sao chép một đối tượng "cũ" nào đó, thì các cài đặt cho nó thường phải được thực hiện lại hoàn toàn. Điều này đặc biệt tốn kém cho các loại đối tượng có nhiều thuộc tính, ví dụ như công cụ Fibonacci. Trong những trường hợp như vậy, script này sẽ hữu ích.
Một số hình ảnh từ chương này, chứa các đối tượng cùng loại, đã được tạo bằng script này.
Chỉ báo ObjectGroupEdit
Ví dụ thứ hai về việc sử dụng ObjectMonitor
là chỉ báo ObjectGroupEdit.mq5
, cho phép chỉnh sửa thuộc tính của một nhóm đối tượng được chọn cùng lúc.
Hãy tưởng tượng chúng ta đã chọn một số đối tượng trên biểu đồ (không nhất thiết cùng loại), mà cần thay đổi đồng nhất một thuộc tính nào đó. Tiếp theo, chúng ta mở hộp thoại thuộc tính của bất kỳ đối tượng nào trong số này, cấu hình nó, và bằng cách nhấp vào OK
, các thay đổi này được áp dụng cho tất cả các đối tượng được chọn. Đây là cách chương trình MQL tiếp theo của chúng ta hoạt động.
Chúng ta cần một chỉ báo làm loại chương trình vì nó liên quan đến các sự kiện biểu đồ. Về khía cạnh lập trình MQL5 này, sẽ có một chương dành riêng, nhưng chúng ta sẽ làm quen với một số điều cơ bản ngay bây giờ.
Vì chỉ báo không có biểu đồ, các chỉ thị #property
chứa số không và hàm OnCalculate
gần như trống rỗng.
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin,
const double &price[])
{
return rates_total;
}
2
3
4
5
6
7
8
9
10
11
Để tự động tạo một tập hợp đầy đủ tất cả các thuộc tính cho một đối tượng, chúng ta sẽ lại sử dụng một mảng 2048 phần tử với các giá trị số nguyên liên tiếp. Chúng ta cũng sẽ cung cấp một mảng cho tên của các phần tử được chọn và một mảng các đối tượng giám sát của lớp ObjectMonitor
.
int consts[2048];
string selected[];
ObjectMonitor *objects[];
2
3
Trong trình xử lý OnInit
, chúng ta khởi tạo mảng số và bắt đầu bộ đếm thời gian.
void OnInit()
{
for(int i = 0; i < ArraySize(consts); ++i)
{
consts[i] = i;
}
EventSetTimer(1);
}
2
3
4
5
6
7
8
9
Trong trình xử lý bộ đếm thời gian, chúng ta lưu tên của các đối tượng được chọn vào một mảng. Nếu danh sách lựa chọn đã thay đổi, cần cấu hình lại các đối tượng giám sát, mà hàm phụ trợ TrackSelectedObjects
được gọi.
void OnTimer()
{
string updates[];
const int n = ObjectsTotal(0);
for(int i = 0; i < n; ++i)
{
const string name = ObjectName(0, i);
if(ObjectGetInteger(0, name, OBJPROP_SELECTED))
{
PUSH(updates, name);
}
}
if(ArraySize(selected) != ArraySize(updates))
{
ArraySwap(selected, updates);
Comment("Selected objects: ", ArraySize(selected));
TrackSelectedObjects();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Chính hàm TrackSelectedObjects
khá đơn giản: xóa các giám sát cũ và tạo mới. Nếu muốn, bạn có thể làm nó thông minh hơn bằng cách duy trì phần không thay đổi của lựa chọn.
void TrackSelectedObjects()
{
for(int j = 0; j < ArraySize(objects); ++j)
{
delete objects[j];
}
ArrayResize(objects, 0);
for(int i = 0; i < ArraySize(selected); ++i)
{
const string name = selected[i];
PUSH(objects, new ObjectMonitor(name, consts));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Nhớ lại rằng khi tạo một đối tượng giám sát, nó ngay lập tức chụp một "bản sao" của tất cả các thuộc tính của đối tượng đồ họa tương ứng.
Bây giờ chúng ta cuối cùng đến phần liên quan đến sự kiện. Như đã đề cập trong tổng quan về các hàm sự kiện, trình xử lý chịu trách nhiệm cho các sự kiện OnChartEvent
trên biểu đồ. Trong ví dụ này, chúng ta quan tâm đến sự kiện cụ thể CHARTEVENT_OBJECT_CHANGE
: nó xảy ra khi người dùng thay đổi bất kỳ thuộc tính nào trong hộp thoại thuộc tính của đối tượng. Tên của đối tượng được sửa đổi được truyền trong tham số sparam
.
Nếu tên này khớp với một trong những đối tượng được giám sát, chúng ta yêu cầu giám sát tạo một bản chụp mới của thuộc tính của nó, tức là gọi objects[i].snapshot()
.
void OnChartEvent(const int id,
const long &lparam, const double &dparam, const string &sparam)
{
if(id == CHARTEVENT_OBJECT_CHANGE)
{
Print("Object changed: ", sparam);
for(int i = 0; i < ArraySize(selected); ++i)
{
if(sparam == selected[i])
{
const int changes = objects[i].snapshot();
if(changes > 0)
{
for(int j = 0; j < ArraySize(objects); ++j)
{
if(j != i)
{
objects[j].applyChanges(objects[i]);
}
}
}
ChartRedraw();
break;
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Nếu các thay đổi được xác nhận (và khó có khả năng ngược lại), số lượng của chúng trong biến changes
sẽ lớn hơn 0. Sau đó, một vòng lặp được bắt đầu qua tất cả các đối tượng được chọn, và các thay đổi được phát hiện được áp dụng cho từng đối tượng, ngoại trừ đối tượng gốc.
Vì chúng ta có thể thay đổi nhiều đối tượng, chúng ta gọi yêu cầu vẽ lại biểu đồ với ChartRedraw
.
Trong trình xử lý OnDeinit
, chúng ta xóa tất cả các giám sát.
void OnDeinit(const int)
{
for(int j = 0; j < ArraySize(objects); ++j)
{
delete objects[j];
}
Comment("");
}
2
3
4
5
6
7
8
Đó là tất cả: công cụ mới đã sẵn sàng.
Chỉ báo này cho phép tùy chỉnh giao diện chung của một số nhóm đối tượng nhãn trong phần về Xác định điểm neo của đối tượng.
Nhân tiện, theo nguyên tắc tương tự với sự trợ giúp của ObjectMonitor
, bạn có thể tạo một công cụ phổ biến khác không có trong terminal: để hoàn tác các chỉnh sửa thuộc tính đối tượng, vì phương thức restore
đã sẵn sàng.