Độ ưu tiên của các đối tượng (Z-Order)
Các đối tượng trên biểu đồ không chỉ cung cấp việc trình bày thông tin mà còn tương tác với người dùng và các chương trình MQL thông qua các sự kiện, điều này sẽ được thảo luận chi tiết trong chương tiếp theo. Một trong những nguồn sự kiện là con trỏ chuột. Biểu đồ có khả năng, đặc biệt, theo dõi chuyển động của chuột và việc nhấn các nút của nó.
Nếu một đối tượng nằm dưới chuột, có thể thực hiện xử lý sự kiện cụ thể cho nó. Tuy nhiên, các đối tượng có thể chồng lấn nhau (khi tọa độ của chúng trùng nhau, tính đến kích thước). Trong trường hợp này, thuộc tính số nguyên OBJPROP_ZORDER
phát huy tác dụng. Nó thiết lập độ ưu tiên của đối tượng đồ họa để nhận sự kiện chuột. Khi các đối tượng chồng lấn, chỉ một đối tượng có độ ưu tiên cao hơn các đối tượng còn lại sẽ nhận được sự kiện.
Theo mặc định, khi một đối tượng được tạo, Z-order của nó là zero, nhưng bạn có thể tăng nó nếu cần thiết.
Cần lưu ý rằng Z-order chỉ ảnh hưởng đến việc xử lý sự kiện chuột, không phải việc vẽ các đối tượng. Các đối tượng luôn được vẽ theo thứ tự chúng được thêm vào biểu đồ. Điều này có thể là nguồn gốc của sự hiểu lầm. Ví dụ, một tooltip có thể không được hiển thị cho một đối tượng nằm trên cùng về mặt hình ảnh vì đối tượng bị chồng lấn có độ ưu tiên Z cao hơn (xem ví dụ).
Trong script ObjectZorder.mq5
, chúng ta sẽ tạo 12 đối tượng loại OBJ_RECTANGLE_LABEL
, đặt chúng theo hình tròn, giống như trên mặt đồng hồ. Thứ tự thêm các đối tượng tương ứng với giờ: từ 1 đến 12. Để rõ ràng, tất cả các hình chữ nhật sẽ được tô màu ngẫu nhiên (đối với thuộc tính OBJPROP_BGCOLOR
, xem phần tiếp theo), cũng như độ ưu tiên ngẫu nhiên. Bằng cách di chuyển chuột qua các đối tượng, người dùng sẽ có thể xác định đối tượng nào thuộc về nó thông qua tooltip.
Để tiện lợi trong việc thiết lập các thuộc tính của đối tượng, chúng ta định nghĩa lớp đặc biệt ObjectBuilder
, được kế thừa từ ObjectSelector
.
#include `ObjectPrefix.mqh`
#include <`MQL5Book/ObjectMonitor.mqh`>
class `ObjectBuilder`: public `ObjectSelector`
{
protected:
const `ENUM_OBJECT` `type`;
const int `window`;
public:
`ObjectBuilder`(const string `_id`, const `ENUM_OBJECT` `_type`,
const long `_chart` = 0, const int `_win` = 0):
`ObjectSelector`(`_id`, `_chart`), `type`(`_type`), `window`(`_win`)
{
`ObjectCreate`(`host`, `id`, `type`, `window`, 0, 0);
}
// thay đổi tên và biểu đồ bị cấm
virtual void `name`(const string `_id`) override = delete;
virtual void `chart`(const long `_chart`) override = delete;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Các trường với định danh của đối tượng (id
) và biểu đồ (host
) đã có trong lớp ObjectSelector
. Trong lớp dẫn xuất, chúng ta thêm loại đối tượng (ENUM_OBJECT type
) và số cửa sổ (int window
). Hàm tạo gọi ObjectCreate
.
Việc thiết lập và đọc thuộc tính được kế thừa hoàn toàn dưới dạng một nhóm phương thức get
và set
từ ObjectSelector
.
Như trong các script kiểm tra trước đó, chúng ta xác định cửa sổ nơi script được thả, kích thước của cửa sổ và tọa độ giữa.
void `OnStart`()
{
const int `t` = `ChartWindowOnDropped`();
int `h` = (int)`ChartGetInteger`(0, `CHART_HEIGHT_IN_PIXELS`, `t`);
int `w` = (int)`ChartGetInteger`(0, `CHART_WIDTH_IN_PIXELS`);
int `x` = `w` / 2;
int `y` = `h` / 2;
...
2
3
4
5
6
7
8
Vì loại đối tượng OBJ_RECTANGLE_LABEL
hỗ trợ kích thước pixel rõ ràng, chúng ta tính toán chiều rộng dx
và chiều cao dy
của mỗi hình chữ nhật bằng một phần tư cửa sổ. Chúng ta sử dụng chúng để thiết lập thuộc tính OBJPROP_XSIZE
và OBJPROP_YSIZE
được thảo luận trong phần Xác định chiều rộng và chiều cao đối tượng.
const int `dx` = `w` / 4;
const int `dy` = `h` / 4;
...
2
3
Tiếp theo, trong vòng lặp, chúng ta tạo 12 đối tượng. Các biến px
và py
chứa độ lệch của "dấu" tiếp theo trên "mặt đồng hồ" so với tâm (x, y). Độ ưu tiên z
được chọn ngẫu nhiên. Tên của đối tượng và tooltip của nó (OBJPROP_TOOLTIP
) bao gồm một chuỗi như "XX - YYY", XX là số "giờ" (vị trí trên mặt đồng hồ từ 1 đến 12), YYY là độ ưu tiên.
for(int `i` = 0; `i` < 12; ++`i`)
{
const int `px` = (int)(`MathSin`((`i` + 1) * 30 * `M_PI` / 180) * `dx`) - `dx` / 2;
const int `py` = -(int)(`MathCos`((`i` + 1) * 30 * `M_PI` / 180) * `dy`) - `dy` / 2;
const int `z` = `rand`();
const string `text` = `StringFormat`("%02d - %d", `i` + 1, `z`);
`ObjectBuilder` *`builder` =
new `ObjectBuilder`(`ObjNamePrefix` + `text`, `OBJ_RECTANGLE_LABEL`);
`builder`.`set`(`OBJPROP_XDISTANCE`, `x` + `px`).`set`(`OBJPROP_YDISTANCE`, `y` + `py`)
.`set`(`OBJPROP_XSIZE`, `dx`).`set`(`OBJPROP_YSIZE`, `dy`)
.`set`(`OBJPROP_TOOLTIP`, `text`)
.`set`(`OBJPROP_ZORDER`, `z`)
.`set`(`OBJPROP_BGCOLOR`, (`rand`() << 8) | `rand`());
delete `builder`;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sau khi hàm tạo ObjectBuilder
được gọi, đối với đối tượng builder
mới, các lệnh gọi đến phương thức set
được nạp chồng cho các thuộc tính khác nhau được nối chuỗi (phương thức set
trả về con trỏ đến chính đối tượng đó).
Vì đối tượng MQL không còn cần thiết sau khi tạo và cấu hình đối tượng đồ họa, chúng ta xóa ngay builder
.
Kết quả của việc thực thi script, khoảng các đối tượng sau sẽ xuất hiện trên biểu đồ.
Hình chú thích: Chồng lấn đối tượng và tooltip độ ưu tiên Z-order
Màu sắc và độ ưu tiên sẽ khác nhau mỗi lần bạn chạy, nhưng sự chồng lấn hình ảnh của các hình chữ nhật sẽ luôn giống nhau, theo thứ tự tạo từ 1 ở dưới cùng đến 12 ở trên cùng (ở đây chúng ta nói về sự chồng lấn của các đối tượng, không phải việc 12 nằm ở trên cùng của mặt đồng hồ).
Trong hình ảnh, con trỏ chuột được đặt ở vị trí có hai đối tượng tồn tại, đó là 01 (xanh lá cây huỳnh quang) và 12 (cát). Trong trường hợp này, tooltip cho đối tượng 01 hiển thị, mặc dù về mặt hình ảnh đối tượng 12 được hiển thị trên đối tượng 01. Điều này là do 01 được tạo ngẫu nhiên với độ ưu tiên cao hơn 12.
Chỉ một tooltip được hiển thị tại một thời điểm, vì vậy bạn có thể kiểm tra mối quan hệ ưu tiên bằng cách di chuyển con trỏ chuột đến các khu vực khác không có chồng lấn đối tượng và thông tin trong tooltip thuộc về đối tượng duy nhất dưới con trỏ.
Khi chúng ta tìm hiểu về xử lý sự kiện chuột trong chương tiếp theo, chúng ta có thể cải thiện ví dụ này và kiểm tra ảnh hưởng của Z-order lên các lần nhấp chuột trên đối tượng.
Để xóa các đối tượng đã tạo, bạn có thể sử dụng script ObjectCleanup1.mq5
.