Cài đặt hiển thị đối tượng: màu sắc, kiểu dáng và khung
Các thuộc tính của đối tượng có thể được thay đổi bằng cách sử dụng nhiều thuộc tính khác nhau, mà chúng ta sẽ khám phá trong phần này, bắt đầu với màu sắc, kiểu dáng, độ dày đường viền và các đường biên. Các khía cạnh định dạng khác như phông chữ, độ nghiêng và căn chỉnh văn bản sẽ được đề cập trong các phần tiếp theo.
Tất cả các thuộc tính trong bảng dưới đây đều có kiểu tương thích với số nguyên và do đó được quản lý bởi các hàm ObjectGetInteger
và ObjectSetInteger
.
Định danh | Mô tả | Kiểu thuộc tính |
---|---|---|
OBJPROP_COLOR | Màu sắc của đường viền và yếu tố chính của đối tượng (ví dụ: phông chữ hoặc màu nền) | color |
OBJPROP_STYLE | Kiểu đường viền | ENUM_LINE_STYLE |
OBJPROP_WIDTH | Độ dày đường viền tính bằng pixel | int |
OBJPROP_FILL | Đổ màu cho đối tượng (dành cho OBJ_RECTANGLE , OBJ_TRIANGLE , OBJ_ELLIPSE , OBJ_CHANNEL , OBJ_STDDEVCHANNEL , OBJ_REGRESSION ) | bool |
OBJPROP_BACK | Đối tượng nằm ở nền | bool |
OBJPROP_BGCOLOR | Màu nền cho OBJ_EDIT , OBJ_BUTTON , OBJ_RECTANGLE_LABEL | color |
OBJPROP_BORDER_TYPE | Kiểu khung cho bảng chữ nhật OBJ_RECTANGLE_LABEL | ENUM_BORDER_TYPE |
OBJPROP_BORDER_COLOR | Màu khung cho trường nhập OBJ_EDIT và nút OBJ_BUTTON | color |
Không giống như hầu hết các đối tượng có đường viền (dọc và ngang riêng biệt, xu hướng, chu kỳ, kênh, v.v.), nơi thuộc tính OBJPROP_COLOR
xác định màu của đường viền, đối với hình ảnh OBJ_BITMAP_LABEL
và OBJ_BITMAP
, nó xác định màu khung, và OBJPROP_STYLE
xác định kiểu vẽ khung.
Chúng ta đã gặp kiểu liệt kê ENUM_LINE_STYLE
, được sử dụng cho OBJPROP_STYLE
, trong chương về chỉ báo, trong phần Cài đặt biểu đồ.
Cần phân biệt việc đổ màu được thực hiện bởi màu tiền cảnh OBJPROP_COLOR
với màu nền OBJPROP_BGCOLOR
. Cả hai được hỗ trợ bởi các nhóm kiểu đối tượng khác nhau, được liệt kê trong bảng.
Thuộc tính OBJPROP_BACK
cần giải thích riêng. Sự thật là các đối tượng và chỉ báo được hiển thị trên biểu đồ giá theo mặc định. Người dùng có thể thay đổi hành vi này cho toàn bộ biểu đồ bằng cách vào hộp thoại Setting
của biểu đồ, tiếp tục đến tab Shared
, tùy chọn Chart on top
. Cờ này cũng có một đối tác phần mềm, thuộc tính CHART_FOREGROUND
(xem Chế độ hiển thị biểu đồ). Tuy nhiên, đôi khi mong muốn không phải loại bỏ tất cả các đối tượng, mà chỉ một số đối tượng được chọn vào nền. Khi đó, bạn có thể đặt OBJPROP_BACK
thành true
. Trong trường hợp này, đối tượng sẽ bị che bởi cả lưới và các đường phân cách kỳ, nếu chúng được bật trên biểu đồ.
Khi chế độ đổ màu OBJPROP_FILL
được bật, màu của các thanh nằm trong hình dạng phụ thuộc vào thuộc tính OBJPROP_BACK
. Theo mặc định, với OBJPROP_BACK
bằng false
, các thanh chồng lên đối tượng được vẽ bằng màu đảo ngược so với OBJPROP_COLOR
(màu đảo ngược được lấy bằng cách chuyển tất cả các bit trong giá trị màu sang giá trị ngược lại, ví dụ: 0x00FF7F
được lấy cho 0xFF0080
). Với OBJPROP_BACK
bằng true
, các thanh được vẽ theo cách thông thường, vì đối tượng được hiển thị ở nền, "dưới" biểu đồ (xem ví dụ dưới đây).
Kiểu liệt kê ENUM_BORDER_TYPE
bao gồm các yếu tố sau:
Định danh | Hình dạng |
---|---|
BORDER_FLAT | Phẳng |
BORDER_RAISED | Lồi |
BORDER_SUNKEN | Lõm |
Khi khung là phẳng (BORDER_FLAT
), nó được vẽ như một đường với màu sắc, kiểu dáng và độ dày theo các thuộc tính OBJPROP_COLOR
, OBJPROP_STYLE
, OBJPROP_WIDTH
. Các phiên bản lồi và lõm mô phỏng các đường viền thể tích xung quanh chu vi trong các sắc thái của OBJPROP_BGCOLOR
.
Khi màu khung OBJPROP_BORDER_COLOR
không được đặt (mặc định, tương ứng với clrNone
), trường nhập được bao quanh bởi một đường của màu chính OBJPROP_COLOR
, và một khung ba chiều với các đường viền trong sắc thái của OBJPROP_BGCOLOR
được vẽ xung quanh nút.
Để kiểm tra các thuộc tính mới, hãy xem xét script ObjectStyle.mq5
. Trong đó, chúng ta sẽ tạo 5 hình chữ nhật kiểu OBJ_RECTANGLE
, tức là liên quan đến thời gian và giá cả. Chúng sẽ được phân bố đều trên toàn bộ chiều rộng của cửa sổ, làm nổi bật phạm vi giữa giá tối đa High
và giá tối thiểu Low
trong mỗi năm khoảng thời gian. Đối với tất cả các đối tượng, chúng ta sẽ điều chỉnh và định kỳ thay đổi màu đường viền, kiểu dáng và độ dày, cũng như tùy chọn đổ màu và hiển thị phía sau biểu đồ.
Hãy sử dụng lại lớp trợ giúp ObjectBuilder
, được dẫn xuất từ ObjectSelector
. Khác với phần trước, chúng ta thêm vào ObjectBuilder
một hàm hủy, trong đó chúng ta sẽ gọi ObjectDelete
.
#include <MQL5Book/ObjectMonitor.mqh>
#include <MQL5Book/AutoPtr.mqh>
class ObjectBuilder: public ObjectSelector
{
...
public:
~ObjectBuilder()
{
ObjectDelete(host, id);
}
...
};
2
3
4
5
6
7
8
9
10
11
12
13
Điều này sẽ cho phép gán cho lớp này không chỉ cấu hình của các đối tượng mà còn việc xóa tự động của chúng khi script kết thúc.
Trong hàm OnStart
, chúng ta tìm ra số lượng thanh hiển thị và chỉ số của thanh đầu tiên, đồng thời tính toán chiều rộng của một hình chữ nhật theo số thanh.
#define OBJECT_NUMBER 5
void OnStart()
{
const string name = "ObjStyle-";
const int bars = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
const int first = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
const int rectsize = bars / OBJECT_NUMBER;
...
}
2
3
4
5
6
7
8
9
10
Hãy dự trữ một mảng các con trỏ thông minh cho các đối tượng để đảm bảo việc gọi các hàm hủy của ObjectBuilder
.
AutoPtr<ObjectBuilder> objects[OBJECT_NUMBER];
Xác định bảng màu và tạo 5 đối tượng hình chữ nhật.
color colors[OBJECT_NUMBER] = {clrRed, clrGreen, clrBlue, clrMagenta, clrOrange};
for(int i = 0; i < OBJECT_NUMBER; ++i)
{
// tìm chỉ số của các thanh xác định phạm vi giá trong khoảng thời gian thứ i
const int h = iHighest(NULL, 0, MODE_HIGH, rectsize, i * rectsize);
const int l = iLowest(NULL, 0, MODE_LOW, rectsize, i * rectsize);
// tạo và thiết lập một đối tượng trong khoảng thời gian thứ i
ObjectBuilder *object = new ObjectBuilder(name + (string)(i + 1), OBJ_RECTANGLE);
object.set(OBJPROP_TIME, iTime(NULL, 0, i * rectsize), 0);
object.set(OBJPROP_TIME, iTime(NULL, 0, (i + 1) * rectsize), 1);
object.set(OBJPROP_PRICE, iHigh(NULL, 0, h), 0);
object.set(OBJPROP_PRICE, iLow(NULL, 0, l), 1);
object.set(OBJPROP_COLOR, colors[i]);
object.set(OBJPROP_WIDTH, i + 1);
object.set(OBJPROP_STYLE, (ENUM_LINE_STYLE)i);
// lưu vào mảng
objects[i] = object;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Ở đây, cho mỗi đối tượng, tọa độ của hai điểm neo được tính toán; màu sắc ban đầu, kiểu dáng và độ dày đường viền được thiết lập.
Tiếp theo, trong một vòng lặp vô hạn, chúng ta thay đổi các thuộc tính của các đối tượng. Khi ScrollLock
được bật, hoạt hình có thể được tạm dừng.
const int key = TerminalInfoInteger(TERMINAL_KEYSTATE_SCRLOCK);
int pass = 0;
int offset = 0;
for(;; !IsStopped(); ++pass)
{
Sleep(200);
if(TerminalInfoInteger(TERMINAL_KEYSTATE_SCRLOCK) != key) continue;
// thay đổi màu sắc/kiểu dáng/độ dày/đổ màu/nền theo thời gian
if(pass % 5 == 0)
{
++offset;
for(int i = 0; i < OBJECT_NUMBER; ++i)
{
objects[i][].set(OBJPROP_COLOR, colors[(i + offset) % OBJECT_NUMBER]);
objects[i][].set(OBJPROP_WIDTH, (i + offset) % OBJECT_NUMBER + 1);
objects[i][].set(OBJPROP_FILL, rand() > 32768 / 2);
objects[i][].set(OBJPROP_BACK, rand() > 32768 / 2);
}
}
ChartRedraw();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Dưới đây là giao diện trên biểu đồ.
Hình chữ nhật OBJ_RECTANGLE với các cài đặt hiển thị khác nhau
Hình chữ nhật màu đỏ ở phía bên trái có chế độ đổ màu được bật và nằm ở tiền cảnh. Do đó, các thanh bên trong nó được hiển thị bằng màu xanh lam sáng tương phản (clrAqua, thường được gọi là cyan, là màu đảo ngược của clrRed). Hình chữ nhật màu tím cũng có chế độ đổ màu, nhưng với tùy chọn nền, nên các thanh trong đó được hiển thị theo cách thông thường.
Xin lưu ý rằng hình chữ nhật màu cam che phủ hoàn toàn các thanh ở phần đầu và cuối của phạm vi con của nó do độ dày đường viền lớn và hiển thị phía trên biểu đồ.
Khi chế độ đổ màu được bật, độ dày đường viền không được tính đến. Khi độ dày đường viền lớn hơn 1, một số kiểu đường nét đứt không được áp dụng.
Vẽ các hình dạng đối tượng (ObjectShapesDraw)
Đối với ví dụ thứ hai trong phần này, hãy nhớ lại chương trình vẽ hình dạng giả định mà chúng ta đã phác thảo trong Phần 3 khi học về lập trình hướng đối tượng (OOP). Tiến độ của chúng ta đã dừng lại ở việc phương thức vẽ ảo (được gọi là draw) chỉ có thể in một thông báo vào nhật ký rằng chúng ta đang vẽ một hình dạng cụ thể. Bây giờ, sau khi làm quen với các đối tượng đồ họa, chúng ta có cơ hội để thực hiện việc vẽ.
Hãy lấy script Shapes5stats.mq5 làm điểm khởi đầu. Phiên bản cập nhật sẽ được gọi là ObjectShapesDraw.mq5.
Nhớ lại rằng ngoài lớp cơ sở Shape, chúng ta đã mô tả một số lớp hình dạng: Rectangle, Ellipse, Triangle, Square, Circle. Tất cả chúng đều khớp tốt với các đối tượng đồ họa kiểu OBJ_RECTANGLE, OBJ_ELLIPSE, OBJ_TRIANGLE. Tuy nhiên, có một số sắc thái.
Tất cả các đối tượng được chỉ định đều gắn với tọa độ thời gian và giá, trong khi chương trình vẽ của chúng ta giả định các trục X và Y thống nhất với định vị điểm. Do đó, chúng ta sẽ cần thiết lập biểu đồ để vẽ theo một cách đặc biệt và sử dụng hàm ChartXYToTimePrice để chuyển đổi các điểm màn hình thành thời gian và giá.
Ngoài ra, các đối tượng OBJ_ELLIPSE và OBJ_TRIANGLE cho phép xoay tự do (đặc biệt, bán kính lớn và nhỏ của hình elip có thể được xoay), trong khi OBJ_RECTANGLE luôn có các cạnh định hướng theo chiều ngang và dọc. Để đơn giản hóa ví dụ, chúng ta giới hạn ở vị trí tiêu chuẩn của tất cả các hình dạng.
Về lý thuyết, việc triển khai mới này nên được xem như một bản demo về các đối tượng đồ họa, chứ không phải là một chương trình vẽ hoàn chỉnh. Một cách tiếp cận đúng đắn hơn cho việc vẽ đầy đủ, không bị hạn chế bởi các giới hạn mà các đối tượng đồ họa áp đặt (vì chúng thường được thiết kế cho các mục đích khác, như đánh dấu biểu đồ), là sử dụng tài nguyên đồ họa. Do đó, chúng ta sẽ quay lại xem xét lại chương trình vẽ trong chương về tài nguyên.
Trong lớp Shape mới, hãy loại bỏ cấu trúc lồng Pair chứa tọa độ đối tượng: cấu trúc này từng là phương tiện để thể hiện một số nguyên tắc của OOP, nhưng giờ đây việc trả lại mô tả ban đầu của các trường int x, y trực tiếp vào lớp Shape sẽ đơn giản hơn. Chúng ta cũng sẽ thêm một trường chứa tên của đối tượng.
class Shape
{
...
protected:
int x, y;
color backgroundColor;
const string type;
string name;
Shape(int px, int py, color back, string t) :
x(px), y(py),
backgroundColor(back),
type(t)
{
}
public:
~Shape()
{
ObjectDelete(0, name);
}
...
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Trường name sẽ cần thiết để thiết lập các thuộc tính của đối tượng đồ họa, cũng như để xóa nó khỏi biểu đồ, điều này hợp lý khi thực hiện trong hàm hủy.
Vì các loại hình dạng khác nhau yêu cầu số lượng điểm hoặc kích thước đặc trưng khác nhau, chúng ta sẽ thêm phương thức setup, ngoài phương thức ảo draw, vào giao diện Shape:
virtual void setup(const int ¶meters[]) = 0;
Nhớ lại rằng trong script, chúng ta đã triển khai một lớp lồng Shape::Registrator, vốn chịu trách nhiệm đếm số lượng hình dạng theo loại. Đã đến lúc giao phó cho nó một nhiệm vụ quan trọng hơn: hoạt động như một nhà máy sản xuất hình dạng. Các lớp hoặc phương thức "nhà máy" rất hữu ích vì chúng cho phép tạo các đối tượng của các lớp khác nhau theo cách thống nhất.
Để làm điều này, chúng ta thêm vào Registrator một phương thức để tạo hình dạng với các tham số bao gồm tọa độ bắt buộc của điểm đầu tiên, một màu sắc, và một mảng các tham số bổ sung (mỗi hình dạng sẽ có thể diễn giải nó theo quy tắc riêng của mình, và trong tương lai, đọc từ hoặc ghi vào tệp).
virtual Shape *create(const int px, const int py, const color back,
const int ¶meters[]) = 0;
2
Phương thức này là ảo trừu tượng vì các loại hình dạng cụ thể chỉ có thể được tạo bởi các lớp đăng ký dẫn xuất được mô tả trong các lớp con của Shape. Để đơn giản hóa việc viết các lớp đăng ký dẫn xuất, chúng ta giới thiệu một lớp mẫu MyRegistrator với triển khai của phương thức create phù hợp cho mọi trường hợp.
template<typename T>
class MyRegistrator : public Shape::Registrator
{
public:
MyRegistrator() : Registrator(typename(T))
{
}
virtual Shape *create(const int px, const int py, const color back,
const int ¶meters[]) override
{
T *temp = new T(px, py, back);
temp.setup(parameters);
return temp;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Ở đây, chúng ta gọi hàm khởi tạo của một hình dạng chưa xác định T, điều chỉnh nó bằng cách gọi setup và trả về một thể hiện cho mã gọi.
Dưới đây là cách nó được sử dụng trong lớp Rectangle, vốn có hai tham số bổ sung cho chiều rộng và chiều cao.
class Rectangle : public Shape
{
static MyRegistrator<Rectangle> r;
protected:
int dx, dy; // kích thước (chiều rộng, chiều cao)
Rectangle(int px, int py, color back, string t) :
Shape(px, py, back, t), dx(1), dy(1)
{
}
public:
Rectangle(int px, int py, color back) :
Shape(px, py, back, typename(this)), dx(1), dy(1)
{
name = typename(this) + (string)r.increment();
}
virtual void setup(const int ¶meters[]) override
{
if(ArraySize(parameters) < 2)
{
Print("Không đủ tham số cho Rectangle");
return;
}
dx = parameters[0];
dy = parameters[1];
}
...
};
static MyRegistrator<Rectangle> Rectangle::r;
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
Khi tạo một hình dạng, tên của nó sẽ chứa không chỉ tên lớp (typename), mà còn số thứ tự của thể hiện, được tính toán trong lời gọi r.increment().
Các lớp hình dạng khác được mô tả tương tự.
Bây giờ, hãy xem xét phương thức draw cho Rectangle. Trong đó, chúng ta chuyển đổi một cặp điểm (x, y) và (x + dx, y + dy) thành tọa độ thời gian/giá bằng ChartXYToTimePrice và tạo một đối tượng OBJ_RECTANGLE.
void draw() override
{
// Print("Drawing rectangle");
int subw;
datetime t;
double p;
ChartXYToTimePrice(0, x, y, subw, t, p);
ObjectCreate(0, name, OBJ_RECTANGLE, 0, t, p);
ChartXYToTimePrice(0, x + dx, y + dy, subw, t, p);
ObjectSetInteger(0, name, OBJPROP_TIME, 1, t);
ObjectSetDouble(0, name, OBJPROP_PRICE, 1, p);
ObjectSetInteger(0, name, OBJPROP_COLOR, backgroundColor);
ObjectSetInteger(0, name, OBJPROP_FILL, true);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Tất nhiên, đừng quên đặt màu sắc cho OBJPROP_COLOR và đổ màu cho OBJPROP_FILL.
Đối với lớp Square, không cần thay đổi gì đặc biệt: chỉ cần đặt dx và dy bằng nhau là đủ.
Đối với lớp Ellipse, hai tham số bổ sung dx và dy xác định bán kính lớn và nhỏ được vẽ tương đối với tâm (x, y). Do đó, trong phương thức draw, chúng ta tính toán 3 điểm neo và tạo một đối tượng OBJ_ELLIPSE.
class Ellipse : public Shape
{
static MyRegistrator<Ellipse> r;
protected:
int dx, dy; // bán kính lớn và nhỏ
...
public:
void draw() override
{
// Print("Drawing ellipse");
int subw;
datetime t;
double p;
// (x, y) tâm
// p0: x + dx, y
// p1: x - dx, y
// p2: x, y + dy
ChartXYToTimePrice(0, x + dx, y, subw, t, p);
ObjectCreate(0, name, OBJ_ELLIPSE, 0, t, p);
ChartXYToTimePrice(0, x - dx, y, subw, t, p);
ObjectSetInteger(0, name, OBJPROP_TIME, 1, t);
ObjectSetDouble(0, name, OBJPROP_PRICE, 1, p);
ChartXYToTimePrice(0, x, y + dy, subw, t, p);
ObjectSetInteger(0, name, OBJPROP_TIME, 2, t);
ObjectSetDouble(0, name, OBJPROP_PRICE, 2, p);
ObjectSetInteger(0, name, OBJPROP_COLOR, backgroundColor);
ObjectSetInteger(0, name, OBJPROP_FILL, true);
}
};
static MyRegistrator<Ellipse> Ellipse::r;
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
Circle là một trường hợp đặc biệt của elip với các bán kính bằng nhau.
Cuối cùng, ở giai đoạn này chỉ hỗ trợ các tam giác đều: kích thước cạnh được chứa trong trường bổ sung dx. Bạn được mời tự tìm hiểu phương thức draw của chúng trong mã nguồn.
Script mới sẽ, như trước đây, tạo ra một số lượng hình dạng ngẫu nhiên nhất định. Chúng được tạo bởi hàm addRandomShape.
Shape *addRandomShape()
{
const int w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
const int h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
const int n = random(Shape::Registrator::getTypeCount());
int cx = 1 + w / 4 + random(w / 2), cy = 1 + h / 4 + random(h / 2);
int clr = ((random(256) << 16) | (random(256) << 8) | random(256));
int custom[] = {1 + random(w / 4), 1 + random(h / 4)};
return Shape::Registrator::get(n).create(cx, cy, clr, custom);
}
2
3
4
5
6
7
8
9
10
11
12
Đây là nơi chúng ta thấy việc sử dụng phương thức nhà máy create, được gọi trên một đối tượng đăng ký được chọn ngẫu nhiên với số n. Nếu sau này chúng ta quyết định thêm các lớp hình dạng khác, logic tạo sẽ không cần thay đổi.
Tất cả các hình dạng được đặt ở phần trung tâm của cửa sổ và có kích thước không lớn hơn một phần tư cửa sổ.
Còn lại là xem xét trực tiếp các lời gọi đến hàm addRandomShape và cài đặt biểu đồ đặc biệt mà chúng ta đã đề cập.
Để cung cấp biểu diễn "vuông" của các điểm trên màn hình, hãy đặt chế độ CHART_SCALEFIX_11. Ngoài ra, chúng ta sẽ chọn tỷ lệ dày đặc nhất (nén) dọc theo trục thời gian CHART_SCALE (0), vì trong đó một thanh chiếm 1 pixel ngang (độ chính xác tối đa). Cuối cùng, tắt hiển thị biểu đồ bằng cách đặt CHART_SHOW thành false.
void OnStart()
{
const int scale = (int)ChartGetInteger(0, CHART_SCALE);
ChartSetInteger(0, CHART_SCALEFIX_11, true);
ChartSetInteger(0, CHART_SCALE, 0);
ChartSetInteger(0, CHART_SHOW, false);
ChartRedraw();
...
}
2
3
4
5
6
7
8
9
Để lưu trữ các hình dạng, hãy dự trữ một mảng các con trỏ thông minh và điền vào đó các hình dạng ngẫu nhiên.
#define FIGURES 21
...
void OnStart()
{
...
AutoPtr<Shape> shapes[FIGURES];
for(int i = 0; i < FIGURES; ++i)
{
Shape *shape = shapes[i] = addRandomShape();
shape.draw();
}
ChartRedraw();
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Sau đó, chúng ta chạy một vòng lặp vô hạn cho đến khi người dùng dừng script, trong đó chúng ta di chuyển nhẹ các hình dạng bằng phương thức move.
while(!IsStopped())
{
Sleep(250);
for(int i = 0; i < FIGURES; ++i)
{
shapes[i][].move(random(20) - 10, random(20) - 10);
shapes[i][].draw();
}
ChartRedraw();
}
...
2
3
4
5
6
7
8
9
10
11
Cuối cùng, chúng ta khôi phục cài đặt biểu đồ.
// chỉ tắt CHART_SCALEFIX_11 là không đủ, cần CHART_SCALEFIX
ChartSetInteger(0, CHART_SCALEFIX, false);
ChartSetInteger(0, CHART_SCALE, scale);
ChartSetInteger(0, CHART_SHOW, true);
}
2
3
4
5
Ảnh chụp màn hình sau đây cho thấy biểu đồ với các hình dạng được vẽ có thể trông như thế nào.
Đối tượng hình dạng trên biểu đồ
Đặc điểm của việc vẽ các đối tượng là sự "nhân đôi" màu sắc ở những nơi chúng chồng lấp nhau.
Vì trục Y đi lên và xuống, tất cả các tam giác đều bị lật ngược, nhưng điều đó không quan trọng, vì chúng ta sẽ làm lại chương trình vẽ dựa trên tài nguyên.