Classi di Tabelle e Intestazioni basate su un modello di tabella in MQL5: Applicazione del concetto MVC
Contenuto
- Introduzione
- Perfezionamento del modello di tabella
- Classe di Intestazione della Tabella
- Classe della Tabella
- Verifica del Risultato.
- Conclusioni
Introduzione
Nel primo articolo riguardante la creazione del Controllo Tabella, abbiamo creato un modello di tabella in MQL5 utilizzando il modello di architettura MVC. Sono state sviluppate classi di celle, righe e modelli di tabelle che hanno consentito di organizzare i dati in una forma comoda e strutturata.
Passiamo ora alla fase successiva: lo sviluppo delle classi della tabella e delle intestazioni della tabella. Le intestazioni di colonna di una tabella non sono semplici etichette di colonna, ma uno strumento per gestire la tabella e le sue colonne. Consentono di aggiungere, eliminare e rinominare colonne. Naturalmente, una tabella può funzionare senza una classe di intestazione, ma poi le sue funzionalità saranno limitate. Verrà creata una semplice tabella statica senza intestazioni di colonna e di conseguenza, senza la funzionalità di controllo delle colonne.
Per implementare la funzionalità di controllo delle colonne è necessario perfezionare il modello della tabella. Lo integreremo con metodi che ti permetteranno di lavorare con le colonne: modificarne la struttura, aggiungerne di nuove o eliminare quelle esistenti. Questi metodi verranno utilizzati dalla classe di intestazione della tabella per fornire un comodo controllo della sua struttura.
Le fasi sviluppate costituiranno la base per l'ulteriore implementazione dei componenti View e Controller, che saranno discussi nei seguenti articoli. Questo passaggio rappresenta un traguardo importante verso la creazione di un'interfaccia completa per i dati operativi.
Perfezionamento del modello di tabella
Al momento, il modello della tabella viene creato da un array bidimensionale, ma per aumentare la flessibilità e la comodità di lavorare con la tabella, aggiungeremo ulteriori metodi di inizializzazione. Ciò consentirà di adattare il modello a diversi scenari di utilizzo. I seguenti metodi appariranno nella versione aggiornata della classe del modello della tabella:
-
Creazione di un modello da un array bidimensionale
void CreateTableModel(T &array[][]);Questo metodo consente di creare rapidamente un modello di tabella basato su un array di dati bidimensionale esistente.
-
Creazione di un modello vuoto con un numero impostato di righe e colonne
void CreateTableModel(const uint num_rows, const uint num_columns);
Questo metodo è adatto quando la struttura della tabella è nota in anticipo, ma i dati verranno aggiunti in seguito.
-
Creazione di un modello da una matrice di dati
void CreateTableModel(const matrix &row_data);
Questo metodo consente di utilizzare una matrice di dati per inizializzare una tabella, il che è comodo quando si lavora con set di dati pre-preparati.
-
Creazione di un modello da un elenco collegato
void CreateTableModel(CList &list_param);In questo caso, per l'archiviazione dei dati verrà utilizzato un array di array, in cui un oggetto CList (dati sulle righe della tabella) contiene altri oggetti CList che contengono dati sulle celle della tabella. Questo approccio consente di controllare dinamicamente la struttura della tabella e il suo contenuto.
Queste modifiche renderanno il modello della tabella più versatile e comodo da utilizzare in vari scenari. Ad esempio, sarà possibile creare facilmente tabelle sia da array di dati pre-preparati sia da elenchi generati dinamicamente.
Nell'ultimo articolo abbiamo descritto tutte le classi per creare un modello di tabella direttamente nel file di script di test. Oggi trasferiremo queste classi nel nostro file include.
Nella cartella in cui è archiviato lo script dell'articolo precedente (per impostazione predefinita: \MQL5\Scripts\TableModel\), creare un nuovo file include denominato Tables.mqh e copiare al suo interno dal file TableModelTest.mq5 che si trova nella stessa cartella, tutto, dall'inizio del file all'inizio del codice dello script di test, ovvero tutte le classi per la creazione di un modello di tabella. Ora abbiamo un file separato con le classi del modello di tabella denominato Tables.mqh. Apportare modifiche e miglioramenti a questo file.
Spostare il file creato in una nuova cartella, MQL5\Scripts\Tables\ - in questa cartella creeremo questo progetto.
Nella sezione dei file/librerie inclusi, aggiungere una dichiarazione anticipata delle nuove classi. Lo faremo oggi e la dichiarazione delle classi è necessaria affinché la classe dell’elenco degli oggetti CListObj creata nell'articolo precedente possa creare oggetti di queste classi nel suo metodo CreateElement():
//+------------------------------------------------------------------+ //| Включаемые библиотеки | //+------------------------------------------------------------------+ #include <Arrays\List.mqh> //--- Форвард-декларация классов class CTableCell; // Класс ячейки таблицы class CTableRow; // Класс строки таблицы class CTableModel; // Класс модели таблицы class CColumnCaption; // Класс заголовка столбца таблицы class CTableHeader; // Класс заголовка таблицы class CTable; // Класс таблицы class CTableByParam; // Класс таблицы на основе массива параметров //+------------------------------------------------------------------+ //| Макросы | //+------------------------------------------------------------------+
Nella sezione macro aggiungere una determinazione della larghezza della cella della tabella in caratteri, pari a 19. Questo è il valore di larghezza minimo in base al quale il testo data-ora si adatta completamente allo spazio della cella nel registro, senza spostare il bordo destro della cella, il che fa sì che le dimensioni di tutte le celle della tabella disegnate nel registro non siano sincronizzate:
//+------------------------------------------------------------------+ //| Макросы | //+------------------------------------------------------------------+ #define MARKER_START_DATA -1 // Маркер начала данных в файле #define MAX_STRING_LENGTH 128 // Максимальная длина строки в ячейке #define CELL_WIDTH_IN_CHARS 19 // Ширина ячейки таблицы в символах //+------------------------------------------------------------------+ //| Перечисления | //+------------------------------------------------------------------+
Nella sezione enumerazione aggiungere nuove costanti all'enumerazione dei tipi di oggetto:
//+------------------------------------------------------------------+ //| Перечисления | //+------------------------------------------------------------------+ enum ENUM_OBJECT_TYPE // Перечисление типов объектов { OBJECT_TYPE_TABLE_CELL=10000, // Ячейка таблицы OBJECT_TYPE_TABLE_ROW, // Строка таблицы OBJECT_TYPE_TABLE_MODEL, // Модель таблицы OBJECT_TYPE_COLUMN_CAPTION, // Заголовок столбца таблицы OBJECT_TYPE_TABLE_HEADER, // Заголовок таблицы OBJECT_TYPE_TABLE, // Таблица OBJECT_TYPE_TABLE_BY_PARAM, // Таблица на данных массива параметров };
Nella classe dell'elenco di oggetti CListObj, nel metodo di creazione degli elementi, aggiungere nuovi casi per la creazione di nuovi tipi di oggetti :
//+------------------------------------------------------------------+ //| Метод создания элемента списка | //+------------------------------------------------------------------+ CObject *CListObj::CreateElement(void) { //--- В зависимости от типа объекта в m_element_type, создаём новый объект switch(this.m_element_type) { case OBJECT_TYPE_TABLE_CELL : return new CTableCell(); case OBJECT_TYPE_TABLE_ROW : return new CTableRow(); case OBJECT_TYPE_TABLE_MODEL : return new CTableModel(); case OBJECT_TYPE_COLUMN_CAPTION : return new CColumnCaption(); case OBJECT_TYPE_TABLE_HEADER : return new CTableHeader(); case OBJECT_TYPE_TABLE : return new CTable(); case OBJECT_TYPE_TABLE_BY_PARAM : return new CTableByParam(); default : return NULL; } }
Dopo aver creato nuove classi, l'elenco degli oggetti CListObj sarà in grado di creare oggetti di questi tipi. Ciò consentirà di salvare e caricare elenchi da file contenenti questi tipi di oggetti.
Per impostare un valore "vuoto" in una cella, che verrà visualizzato come una riga vuota e non come un valore "0", come avviene ora, è necessario determinare quale valore deve essere considerato "vuoto". È chiaro che per i valori stringa, una riga vuota sarà un valore di questo tipo. Mentre per i valori numerici definire DBL_MAX per i tipi reali e LONG_MAX per gli interi.
Per impostare tale valore, nella classe oggetto di una cella di tabella, nell'area protected, scrivere il seguente metodo :
class CTableCell : public CObject { protected: //--- Объединение для хранения значений ячейки (double, long, string) union DataType { protected: double double_value; long long_value; ushort ushort_value[MAX_STRING_LENGTH]; public: //--- Установка значений void SetValueD(const double value) { this.double_value=value; } void SetValueL(const long value) { this.long_value=value; } void SetValueS(const string value) { ::StringToShortArray(value,ushort_value); } //--- Возврат значений double ValueD(void) const { return this.double_value; } long ValueL(void) const { return this.long_value; } string ValueS(void) const { string res=::ShortArrayToString(this.ushort_value); res.TrimLeft(); res.TrimRight(); return res; } }; //--- Переменные DataType m_datatype_value; // Значение ENUM_DATATYPE m_datatype; // Тип данных CObject *m_object; // Объект в ячейке ENUM_OBJECT_TYPE m_object_type; // Тип объекта в ячейке int m_row; // Номер строки int m_col; // Номер столбца int m_digits; // Точность представления данных uint m_time_flags; // Флаги отображения даты/времени bool m_color_flag; // Флаг отображения наименования цвета bool m_editable; // Флаг редактируемой ячейки //--- Устанавливает "пустое значение" void SetEmptyValue(void) { switch(this.m_datatype) { case TYPE_LONG : case TYPE_DATETIME: case TYPE_COLOR : this.SetValue(LONG_MAX); break; case TYPE_DOUBLE : this.SetValue(DBL_MAX); break; default : this.SetValue(""); break; } } public: //--- Возврат координат и свойств ячейки
Il metodo restituisce il valore memorizzato in una cella come stringa formattata, ora controlla che il valore nella cella non sia "vuoto" e, se il valore è "vuoto", restituisce una riga vuota :
public: //--- Возврат координат и свойств ячейки uint Row(void) const { return this.m_row; } uint Col(void) const { return this.m_col; } ENUM_DATATYPE Datatype(void) const { return this.m_datatype; } int Digits(void) const { return this.m_digits; } uint DatetimeFlags(void) const { return this.m_time_flags; } bool ColorNameFlag(void) const { return this.m_color_flag; } bool IsEditable(void) const { return this.m_editable; } //--- Возвращает (1) double, (2) long, (3) string значение double ValueD(void) const { return this.m_datatype_value.ValueD(); } long ValueL(void) const { return this.m_datatype_value.ValueL(); } string ValueS(void) const { return this.m_datatype_value.ValueS(); } //--- Возвращает значение в виде форматированной строки string Value(void) const { switch(this.m_datatype) { case TYPE_DOUBLE : return(this.ValueD()!=DBL_MAX ? ::DoubleToString(this.ValueD(),this.Digits()) : ""); case TYPE_LONG : return(this.ValueL()!=LONG_MAX ? ::IntegerToString(this.ValueL()) : ""); case TYPE_DATETIME: return(this.ValueL()!=LONG_MAX ? ::TimeToString(this.ValueL(),this.m_time_flags) : ""); case TYPE_COLOR : return(this.ValueL()!=LONG_MAX ? ::ColorToString((color)this.ValueL(),this.m_color_flag) : ""); default : return this.ValueS(); } } //--- Возвращает описание типа хранимого значения string DatatypeDescription(void) const { string type=::StringSubstr(::EnumToString(this.m_datatype),5); type.Lower(); return type; } //--- Очищает данные void ClearData(void) { this.SetEmptyValue(); }
Il metodo per cancellare i dati in una cella ora non imposta più zero, ma chiama un metodo per impostare un valore vuoto nella cella.
I metodi di tutte le classi da cui viene creato l'oggetto modello di tabella, che restituiscono la descrizione dell'oggetto, ora vengono resi virtuali, in caso di ereditarietà da questi oggetti:
//--- (1) Возвращает, (2) выводит в журнал описание объекта virtual string Description(void); void Print(void);
Nella classe della riga della tabella, tutti i metodi CreateNewCell() che creano una nuova cella e la aggiungono alla fine dell'elenco sono stati ora rinominati :
//+------------------------------------------------------------------+ //| Класс строки таблицы | //+------------------------------------------------------------------+ class CTableRow : public CObject { protected: CTableCell m_cell_tmp; // Объект ячейки для поиска в списке CListObj m_list_cells; // Список ячеек uint m_index; // Индекс строки //--- Добавляет указанную ячейку в конец списка bool AddNewCell(CTableCell *cell); public: //--- (1) Устанавливает, (2) возвращает индекс строки void SetIndex(const uint index) { this.m_index=index; } uint Index(void) const { return this.m_index; } //--- Устанавливает позиции строки и колонки всем ячейкам void CellsPositionUpdate(void); //--- Создаёт новую ячейку и добавляет в конец списка CTableCell *CellAddNew(const double value); CTableCell *CellAddNew(const long value); CTableCell *CellAddNew(const datetime value); CTableCell *CellAddNew(const color value); CTableCell *CellAddNew(const string value);
Ciò avviene in modo che tutti i metodi responsabili dell'accesso alle celle inizino con la sottostringa "Cell". In altre classi, i metodi per accedere, ad esempio, a una riga di una tabella inizieranno con la sottostringa "Row". Ciò porta ordine nei metodi delle classi.
Per costruire un modello di tabella, dobbiamo sviluppare un approccio universale che consentirà di creare tabelle da quasi tutti i dati. Possono essere, ad esempio, strutture, elenchi di operazioni, ordini, posizioni o qualsiasi altro dato. L'idea è quella di creare un pacchetto di strumenti che consenta di creare un elenco di righe, in cui ogni riga sarà un elenco di proprietà. Ogni proprietà nell'elenco corrisponderà a una cella della tabella.
Qui, occorre prestare attenzione alla struttura dei parametri di input di MqlParam. Offre le seguenti caratteristiche:
- Specifica il tipo di dati memorizzati nella struttura (ENUM_DATATYPE).
- Memorizzazione dei valori in tre campi:
- integer_value — per dati interi,
- double_value — per dati reali,
- string_value — per dati stringa.
Questa struttura consente di lavorare con vari tipi di dati, consentendo di memorizzare qualsiasi proprietà, come parametri di transazioni, ordini o altri oggetti.
Per una comoda archiviazione dei dati, creare la classe CMqlParamObj, ereditata dalla classe base CObject della Libreria Standard. Questa classe includerà la struttura MqlParam e fornirà metodi per impostare e recuperare i dati. Ereditando da CObject, tali oggetti possono essere memorizzati negli elenchi CList.
Considera l'intera classe:
//+------------------------------------------------------------------+ //| Класс объекта параметра структуры | //+------------------------------------------------------------------+ class CMqlParamObj : public CObject { protected: public: MqlParam m_param; //--- Установка параметров void Set(const MqlParam ¶m) { this.m_param.type=param.type; this.m_param.double_value=param.double_value; this.m_param.integer_value=param.integer_value; this.m_param.string_value=param.string_value; } //--- Возврат параметров MqlParam Param(void) const { return this.m_param; } ENUM_DATATYPE Datatype(void) const { return this.m_param.type; } double ValueD(void) const { return this.m_param.double_value; } long ValueL(void) const { return this.m_param.integer_value;} string ValueS(void) const { return this.m_param.string_value; } //--- Описание объекта virtual string Description(void) { string t=::StringSubstr(::EnumToString(this.m_param.type),5); t.Lower(); string v=""; switch(this.m_param.type) { case TYPE_STRING : v=this.ValueS(); break; case TYPE_FLOAT : case TYPE_DOUBLE : v=::DoubleToString(this.ValueD()); break; case TYPE_DATETIME: v=::TimeToString(this.ValueL(),TIME_DATE|TIME_MINUTES|TIME_SECONDS); break; default : v=(string)this.ValueL(); break; } return(::StringFormat("<%s>%s",t,v)); } //--- Конструкторы/деструктор CMqlParamObj(void){} CMqlParamObj(const MqlParam ¶m) { this.Set(param); } ~CMqlParamObj(void){} };
Si tratta di un wrapper comune attorno alla struttura MqlParam, poiché richiede un oggetto ereditato da CObject per memorizzare tali oggetti nell'elenco CList.
La struttura dei dati creati sarà la seguente:
- Un oggetto della classe CMqlParamObj rappresenterà una proprietà, ad esempio il prezzo di un'operazione, il suo volume o l'orario di apertura.
- Un singolo elenco CList rappresenterà una riga della tabella contenente tutte le proprietà di una singola operazione.
- L'elenco CList principale conterrà un insieme di righe (elenchi CList), ciascuna delle quali corrisponde a un'operazione, un ordine, una posizione o qualsiasi altra entità.
Otterremo quindi una struttura simile a un array di array:
- L'elenco CList principale è un "array di righe"
- Ogni elenco CList annidato è un "array di celle" (proprietà di alcuni oggetti).
Ecco un esempio di tale struttura dati per un elenco di operazioni storiche:
- L'elenco CList principale memorizza le righe di una tabella. Ogni riga è un elenco CList separato.
- Elenchi annidati CList - ogni elenco annidato rappresenta una riga in una tabella e contiene oggetti della classe CMqlParamObj che memorizzano le proprietà. Per esempio:
- riga uno: proprietà del trade No. 1 (prezzo, volume, orario di apertura, ecc.),
- riga due: proprietà del trade No. 2 (prezzo, volume, orario di apertura, ecc.),
- riga tre: proprietà del trade No. 3 (prezzo, volume, orario di apertura, ecc.),
- ecc.
- Proprietà degli oggetti (CMqlParamObj) - ogni oggetto memorizza una proprietà, ad esempio il prezzo di un trade o il suo volume.
Dopo aver formato la struttura dati (elenchi CList), è possibile passarla al metodo CreateTableModel (CList &list_param) del modello della tabella. Questo metodo interpreterà i dati come segue:
- L'elenco CList principale è un elenco di righe della tabella,
- Ogni elenco CList annidato è costituito da celle di ogni riga,
- Gli oggetti CMqlParamObj all'interno degli elenchi annidati sono valori di cella.
In questo modo, sulla base dell'elenco passato, verrà creata una tabella che corrisponde pienamente ai dati originali.
Per facilitare la creazione di tali elenchi, sviluppare una classe speciale. Questa classe fornirà metodi per:
- Creare una nuova riga (dell'elenco CList) e aggiungerla all'elenco principale,
- Aggiunta di una nuova proprietà (oggetto CMqlParamObj) alla riga.
//+------------------------------------------------------------------+ //| Класс для создания списков данных | //+------------------------------------------------------------------+ class DataListCreator { public: //--- Добавляет новую строку к списку CList list_data static CList *AddNewRowToDataList(CList *list_data) { CList *row=new CList; if(row==NULL || list_data.Add(row)<0) return NULL; return row; } //--- Создаёт новый объект параметров CMqlParamObj и добавляет его к списку CList static bool AddNewCellParamToRow(CList *row,MqlParam ¶m) { CMqlParamObj *cell=new CMqlParamObj(param); if(cell==NULL) return false; if(row.Add(cell)<0) { delete cell; return false; } return true; } };
Questa è una classe statica che fornisce una funzionalità per creare in modo pratico elenchi con la struttura corretta per passarli ai metodi di creazione dei modelli di tabella:
- Specificare la proprietà necessaria (ad esempio, il prezzo di trade)
- La classe crea automaticamente un oggetto CMqlParamObj, scrive il valore della proprietà su di esso e lo aggiunge alla riga,
- La riga viene aggiunta all'elenco principale.
Dopodiché, l'elenco completato può essere passato al modello della tabella per lo sviluppo.
Di conseguenza, l'approccio sviluppato consente di convertire i dati da qualsiasi struttura o oggetto in un formato adatto alla creazione di una tabella. L'utilizzo di elenchi CList e oggetti CMqlParamObj garantisce flessibilità e praticità di utilizzo, mentre la classe ausiliaria DataListCreator semplifica il processo di creazione di tali elenchi. Questa sarà la base per costruire un modello di tabella universale, creato a partire da qualsiasi dato e per qualsiasi attività.
Nella classe dei modelli di tabella, aggiungere tre nuovi metodi per creare un modello di tabella e tre metodi per la gestione delle colonne. Invece di cinque costruttori parametrici sovraccaricati, aggiungere un costruttore di template e tre nuovi costruttori basati sui tipi di parametri di input dei nuovi metodi per la creazione di un modello di tabella :
//+------------------------------------------------------------------+ //| Класс модели таблицы | //+------------------------------------------------------------------+ class CTableModel : public CObject { protected: CTableRow m_row_tmp; // Объект строки для поиска в списке CListObj m_list_rows; // Список строк таблицы //--- Создаёт модель таблицы из двумерного массива template<typename T> void CreateTableModel(T &array[][]); void CreateTableModel(const uint num_rows,const uint num_columns); void CreateTableModel(const matrix &row_data); void CreateTableModel(CList &list_param); //--- Возвращает корректный тип данных ENUM_DATATYPE GetCorrectDatatype(string type_name) { return ( //--- Целочисленное значение type_name=="bool" || type_name=="char" || type_name=="uchar" || type_name=="short"|| type_name=="ushort" || type_name=="int" || type_name=="uint" || type_name=="long" || type_name=="ulong" ? TYPE_LONG : //--- Вещественное значение type_name=="float"|| type_name=="double" ? TYPE_DOUBLE : //--- Значение даты/времени type_name=="datetime" ? TYPE_DATETIME : //--- Значение цвета type_name=="color" ? TYPE_COLOR : /*--- Строковое значение */ TYPE_STRING ); } //--- Создаёт и добавляет новую пустую строку в конец списка CTableRow *CreateNewEmptyRow(void); //--- Добавляет строку в конец списка bool AddNewRow(CTableRow *row); //--- Устанавливает позиции строки и колонки всем ячейкам таблицы void CellsPositionUpdate(void); public: //--- Возвращает (1) ячейку, (2) строку по индексу, количество (3) строк, ячеек (4) в указанной строке, (5) в таблице CTableCell *GetCell(const uint row, const uint col); CTableRow *GetRow(const uint index) { return this.m_list_rows.GetNodeAtIndex(index); } uint RowsTotal(void) const { return this.m_list_rows.Total(); } uint CellsInRow(const uint index); uint CellsTotal(void); //--- Устанавливает (1) значение, (2) точность, (3) флаги отображения времени, (4) флаг отображения имён цветов в указанную ячейку template<typename T> void CellSetValue(const uint row, const uint col, const T value); void CellSetDigits(const uint row, const uint col, const int digits); void CellSetTimeFlags(const uint row, const uint col, const uint flags); void CellSetColorNamesFlag(const uint row, const uint col, const bool flag); //--- (1) Назначает, (2) отменяет объект в ячейке void CellAssignObject(const uint row, const uint col,CObject *object); void CellUnassignObject(const uint row, const uint col); //--- (1) Удаляет (2) перемещает ячейку bool CellDelete(const uint row, const uint col); bool CellMoveTo(const uint row, const uint cell_index, const uint index_to); //--- (1) Возвращает, (2) выводит в журнал описание ячейки, (3) назначенный в ячейку объект string CellDescription(const uint row, const uint col); void CellPrint(const uint row, const uint col); CObject *CellGetObject(const uint row, const uint col); public: //--- Создаёт новую строку и (1) добавляет в конец списка, (2) вставляет в указанную позицию списка CTableRow *RowAddNew(void); CTableRow *RowInsertNewTo(const uint index_to); //--- (1) Удаляет (2) перемещает строку, (3) очищает данные строки bool RowDelete(const uint index); bool RowMoveTo(const uint row_index, const uint index_to); void RowClearData(const uint index); //--- (1) Возвращает, (2) выводит в журнал описание строки string RowDescription(const uint index); void RowPrint(const uint index,const bool detail); //--- (1) Добавляет, (2) удаляет (3) перемещает столбец, (4) очищает данные, устанавливает (5) тип, (6) точность данных столбца bool ColumnAddNew(const int index=-1); bool ColumnDelete(const uint index); bool ColumnMoveTo(const uint col_index, const uint index_to); void ColumnClearData(const uint index); void ColumnSetDatatype(const uint index,const ENUM_DATATYPE type); void ColumnSetDigits(const uint index,const int digits); //--- (1) Возвращает, (2) выводит в журнал описание таблицы virtual string Description(void); void Print(const bool detail); void PrintTable(const int cell_width=CELL_WIDTH_IN_CHARS); //--- (1) Очищает данные, (2) уничтожает модель void ClearData(void); void Destroy(void); //--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта virtual int Compare(const CObject *node,const int mode=0) const; virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); virtual int Type(void) const { return(OBJECT_TYPE_TABLE_MODEL); } //--- Конструкторы/деструктор template<typename T> CTableModel(T &array[][]) { this.CreateTableModel(array); } CTableModel(const uint num_rows,const uint num_columns) { this.CreateTableModel(num_rows,num_columns); } CTableModel(const matrix &row_data) { this.CreateTableModel(row_data); } CTableModel(CList &row_data) { this.CreateTableModel(row_data); } CTableModel(void){} ~CTableModel(void){} };
Consideriamo i nuovi metodi.
Un metodo che crea un modello di tabella dal numero specificato di righe e colonne
//+------------------------------------------------------------------+ //| Создаёт модель таблицы из указанного количества строк и столбцов | //+------------------------------------------------------------------+ void CTableModel::CreateTableModel(const uint num_rows,const uint num_columns) { //--- В цикле по количеству строк for(uint r=0; r<num_rows; r++) { //--- создаём новую пустую строку и добавляем её в конец списка строк CTableRow *row=this.CreateNewEmptyRow(); //--- Если строка создана и добавлена в список, if(row!=NULL) { //--- В цикле по количеству столбцов //--- создаём все ячейки, добавляя каждую новую в конец списка ячеек строки for(uint c=0; c<num_columns; c++) { CTableCell *cell=row.CellAddNew(0.0); if(cell!=NULL) cell.ClearData(); } } } }
Il metodo crea un modello vuoto con il numero specificato di righe e colonne. È adatto quando la struttura della tabella è nota in anticipo, ma si prevede che i dati vengano aggiunti in un secondo momento.
Un metodo che crea un modello di tabella dalla matrice specificata
//+------------------------------------------------------------------+ //| Создаёт модель таблицы из указанной матрицы | //+------------------------------------------------------------------+ void CTableModel::CreateTableModel(const matrix &row_data) { //--- Количество строк и столбцов ulong num_rows=row_data.Rows(); ulong num_columns=row_data.Cols(); //--- В цикле по количеству строк for(uint r=0; r<num_rows; r++) { //--- создаём новую пустую строку и добавляем её в конец списка строк CTableRow *row=this.CreateNewEmptyRow(); //--- Если строка создана и добавлена в список, if(row!=NULL) { //--- В цикле по количеству столбцов //--- создаём все ячейки, добавляя каждую новую в конец списка ячеек строки for(uint c=0; c<num_columns; c++) row.CellAddNew(row_data[r][c]); } } }
Il metodo consente di utilizzare una matrice di dati per inizializzare una tabella, il che è comodo per operare su set di dati pre-preparati.
Un metodo che crea un modello di tabella dall'elenco dei parametri
//+------------------------------------------------------------------+ //| Создаёт модель таблицы из списка параметров | //+------------------------------------------------------------------+ void CTableModel::CreateTableModel(CList &list_param) { //--- Если передан пустой список - сообщаем об этом и уходим if(list_param.Total()==0) { ::PrintFormat("%s: Error. Empty list passed",__FUNCTION__); return; } //--- Получаем указатель на первую строку таблицы для определения количества столбцов //--- Если первую строку получить не удалось, или в ней нет ячеек - сообщаем об этом и уходим CList *first_row=list_param.GetFirstNode(); if(first_row==NULL || first_row.Total()==0) { if(first_row==NULL) ::PrintFormat("%s: Error. Failed to get first row of list",__FUNCTION__); else ::PrintFormat("%s: Error. First row does not contain data",__FUNCTION__); return; } //--- Количество строк и столбцов ulong num_rows=list_param.Total(); ulong num_columns=first_row.Total(); //--- В цикле по количеству строк for(uint r=0; r<num_rows; r++) { //--- получаем очередную строку таблицы из списка list_param CList *col_list=list_param.GetNodeAtIndex(r); if(col_list==NULL) continue; //--- создаём новую пустую строку и добавляем её в конец списка строк CTableRow *row=this.CreateNewEmptyRow(); //--- Если строка создана и добавлена в список, if(row!=NULL) { //--- В цикле по количеству столбцов //--- создаём все ячейки, добавляя каждую новую в конец списка ячеек строки for(uint c=0; c<num_columns; c++) { CMqlParamObj *param=col_list.GetNodeAtIndex(c); if(param==NULL) continue; //--- Объявляем указатель на ячейку и тип данных, которые будут в ней содержаться CTableCell *cell=NULL; ENUM_DATATYPE datatype=param.Datatype(); //--- В зависимости от типа данных switch(datatype) { //--- вещественный тип данных case TYPE_FLOAT : case TYPE_DOUBLE : cell=row.CellAddNew((double)param.ValueD()); // Создаём новую ячейку с double-данными и if(cell!=NULL) cell.SetDigits((int)param.ValueL()); // записываем точность отображаемых данных break; //--- тип данных datetime case TYPE_DATETIME: cell=row.CellAddNew((datetime)param.ValueL()); // Создаём новую ячейку с datetime-данными и if(cell!=NULL) cell.SetDatetimeFlags((int)param.ValueD()); // записываем флаги отображения даты/времени break; //--- тип данных color case TYPE_COLOR : cell=row.CellAddNew((color)param.ValueL()); // Создаём новую ячейку с color-данными и if(cell!=NULL) cell.SetColorNameFlag((bool)param.ValueD()); // записваем флаг отображения наименования известных цветов break; //--- строковый тип данных case TYPE_STRING : cell=row.CellAddNew((string)param.ValueS()); // Создаём новую ячейку со string-данными break; //--- целочисленный тип данных default : cell=row.CellAddNew((long)param.ValueL()); // Создаём новую ячейку с long-данными break; } } } } }
Questo metodo consente di creare un modello di tabella basato su un elenco collegato, utile per gestire strutture dati dinamiche.
Vale la pena notare che quando si creano celle con un tipo di dati, ad esempio double, la precisione di visualizzazione viene presa dal valore long dell'oggetto param della classe CMqlParamObj. Ciò significa che quando si crea una struttura di tabella utilizzando la classe DataListCreator come discusso in precedenza, possiamo inoltre passare le informazioni di chiarimento necessarie all'oggetto parametro. Per ottenere il risultato finanziario di un'operazione, è possibile procedere come segue:
//--- Финансовый результат сделки param.type=TYPE_DOUBLE; param.double_value=HistoryDealGetDouble(ticket,DEAL_PROFIT); param.integer_value=(param.double_value!=0 ? 2 : 1); DataListCreator::AddNewCellParamToRow(row,param);
Allo stesso modo, è possibile passare flag di visualizzazione dell'ora per le celle della tabella con il tipo datetime e un flag per visualizzare il nome del colore per una cella con il tipo color.
La tabella mostra i tipi di celle della tabella e i tipi di parametri passati tramite l'oggetto CMqlParamObj:
| Tipo CMqlParamObj | Tipo di cella double | Tipo di cella long | Tipo di cella datetime | Tipo di cella color | Tipo di cella string |
|---|---|---|---|---|---|
| double_value | valore della cella | non utilizzato | flag di visualizzazione data/ora | flag di visualizzazione del nome del colore | non utilizzato |
| integer_value | precisione del valore della cella | valore della cella | valore della cella | valore della cella | non utilizzato |
| string_value | non utilizzato | non utilizzato | non utilizzato | non utilizzato | valore della cella |
La tabella mostra che quando si crea una struttura di tabella da alcuni dati, se questi dati sono di tipo reale (scritti nel campo della struttura double_value di MqlParam), allora è possibile scrivere anche nel campo integer_value il valore di precisione con cui i dati verranno visualizzati nella cella della tabella. Lo stesso vale per i dati con i tipi datetime e color, ma i flag vengono scritti nel campo double_value, poiché il campo integer è occupato dal valore della proprietà stessa.
Questo è facoltativo. Allo stesso tempo, i valori dei flag e della precisione nella cella verranno impostati su zero. Questo valore può poi essere modificato sia per una cella specifica che per l'intera colonna della tabella.
Un metodo che aggiunge una nuova colonna a una tabella
//+------------------------------------------------------------------+ //| Добавляет столбец | //+------------------------------------------------------------------+ bool CTableModel::ColumnAddNew(const int index=-1) { //--- Объявляем переменные CTableCell *cell=NULL; bool res=true; //--- В цикле по количеству строк for(uint i=0;i<this.RowsTotal();i++) { //--- получаем очередную строку CTableRow *row=this.GetRow(i); if(row!=NULL) { //--- добавляем в конец строки ячейку с типом double cell=row.CellAddNew(0.0); if(cell==NULL) res &=false; //--- очищаем ячейку else cell.ClearData(); } } //--- Если передан индекс колонки не отрицательный - сдвигаем колонку на указанную позицию if(res && index>-1) res &=this.ColumnMoveTo(this.CellsInRow(0)-1,index); //--- Возвращаем результат return res; }
L'indice della nuova colonna viene passato al metodo. Innanzitutto, le nuove celle vengono aggiunte alla fine della riga in tutte le righe della tabella e poi, se l'indice passato non è negativo, tutte le nuove celle vengono spostate tramite l'indice specificato.
Un metodo che imposta il tipo di dati della colonna
//+------------------------------------------------------------------+ //| Устанавливает тип данных столбца | //+------------------------------------------------------------------+ void CTableModel::ColumnSetDatatype(const uint index,const ENUM_DATATYPE type) { //--- В цикле по всем строкам таблицы for(uint i=0;i<this.RowsTotal();i++) { //--- получаем из каждой строки ячейку с индексом столбца и устанавливаем тип данных CTableCell *cell=this.GetCell(i, index); if(cell!=NULL) cell.SetDatatype(type); } }
In un ciclo attraverso tutte le righe della tabella, ottenere una cella di ogni riga in base all'indice e impostarne un tipo di dati. Di conseguenza, nelle celle dell'intera colonna viene impostato un valore identico.
Un metodo che imposta l'accuratezza dei dati della colonna
//+------------------------------------------------------------------+ //| Устанавливает точность данных столбца | //+------------------------------------------------------------------+ void CTableModel::ColumnSetDigits(const uint index,const int digits) { //--- В цикле по всем строкам таблицы for(uint i=0;i<this.RowsTotal();i++) { //--- получаем из каждой строки ячейку с индексом столбца и устанавливаем точность данных CTableCell *cell=this.GetCell(i, index); if(cell!=NULL) cell.SetDigits(digits); } }
In un ciclo attraverso tutte le righe della tabella, ottenere una cella di ogni riga in base all'indice e impostarne un tipo di dati. Di conseguenza, lo stesso valore viene impostato nelle celle dell'intera colonna.
I metodi aggiunti alla classe del modello della tabella consentiranno di operare con un insieme di celle come se fosse un'intera colonna di tabella. Le colonne di una tabella possono essere controllate solo se la tabella ha un'intestazione. La tabella sarà statica senza l'intestazione.
Classe di Intestazione della Tabella
L'intestazione della tabella è un elenco di oggetti di intestazione di colonna con un valore stringa. Si trovano in un elenco dinamico CListObj. E l'elenco dinamico costituisce la base della classe di intestazione della tabella.
Sulla base di ciò, dovranno essere create due classi:
- La classe dell'oggetto intestazione della colonna della tabella.
Contiene il valore di testo dell'intestazione, il numero di colonna, il tipo di dato per l'intera colonna e i metodi per controllare le celle della colonna. - La classe dell'intestazione della tabella.
Contiene un elenco di oggetti di intestazione di colonna e metodi di accesso per il controllo delle colonne della tabella.
Continua a scrivere il codice nello stesso file \MQL5\Scripts\Tables\Tables.mqh e scrivi la classe dell'intestazione della colonna della tabella:
//+------------------------------------------------------------------+ //| Класс заголовка столбца таблицы | //+------------------------------------------------------------------+ class CColumnCaption : public CObject { protected: //--- Переменные ushort m_ushort_array[MAX_STRING_LENGTH]; // Массив символов заголовка uint m_column; // Номер столбца ENUM_DATATYPE m_datatype; // Тип данных public: //--- (1) Устанавливает, (2) возвращает номер столбца void SetColumn(const uint column) { this.m_column=column; } uint Column(void) const { return this.m_column; } //--- (1) Устанавливает, (2) возвращает тип данных столбца ENUM_DATATYPE Datatype(void) const { return this.m_datatype; } void SetDatatype(const ENUM_DATATYPE datatype) { this.m_datatype=datatype;} //--- Очищает данные void ClearData(void) { this.SetValue(""); } //--- Устанавливает заголовок void SetValue(const string value) { ::StringToShortArray(value,this.m_ushort_array); } //--- Возвращает текст заголовка string Value(void) const { string res=::ShortArrayToString(this.m_ushort_array); res.TrimLeft(); res.TrimRight(); return res; } //--- (1) Возвращает, (2) выводит в журнал описание объекта virtual string Description(void); void Print(void); //--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта virtual int Compare(const CObject *node,const int mode=0) const; virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); virtual int Type(void) const { return(OBJECT_TYPE_COLUMN_CAPTION); } //--- Конструкторы/деструктор CColumnCaption(void) : m_column(0) { this.SetValue(""); } CColumnCaption(const uint column,const string value) : m_column(column) { this.SetValue(value); } ~CColumnCaption(void) {} };
Questa è una versione molto semplificata della classe cella della tabella. Consideriamo alcuni metodi della classe.
Un metodo virtuale per confrontare due oggetti
//+------------------------------------------------------------------+ //| Сравнение двух объектов | //+------------------------------------------------------------------+ int CColumnCaption::Compare(const CObject *node,const int mode=0) const { const CColumnCaption *obj=node; return(this.Column()>obj.Column() ? 1 : this.Column()<obj.Column() ? -1 : 0); }
Il confronto viene effettuato tramite l'indice della colonna per la quale è stata creata l'intestazione.
Un metodo per salvare in un file
//+------------------------------------------------------------------+ //| Сохранение в файл | //+------------------------------------------------------------------+ bool CColumnCaption::Save(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Сохраняем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileWriteLong(file_handle,MARKER_START_DATA)!=sizeof(long)) return(false); //--- Сохраняем тип объекта if(::FileWriteInteger(file_handle,this.Type(),INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем номер столбца if(::FileWriteInteger(file_handle,this.m_column,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем значение if(::FileWriteArray(file_handle,this.m_ushort_array)!=sizeof(this.m_ushort_array)) return(false); //--- Всё успешно return true; }
Un metodo per scaricare da un file
//+------------------------------------------------------------------+ //| Загрузка из файла | //+------------------------------------------------------------------+ bool CColumnCaption::Load(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Загружаем и проверяем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return(false); //--- Загружаем тип объекта if(::FileReadInteger(file_handle,INT_VALUE)!=this.Type()) return(false); //--- Загружаем номер столбца this.m_column=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем значение if(::FileReadArray(file_handle,this.m_ushort_array)!=sizeof(this.m_ushort_array)) return(false); //--- Всё успешно return true; }
Metodi simili sono stati discussi in dettaglio nell'articolo precedente. La logica qui è esattamente la stessa: prima vengono registrati i marcatori di inizio dati e il tipo di oggetto. E poi, tutte le sue proprietà, elemento per elemento. La lettura avviene nello stesso ordine.
Un metodo che restituisce la descrizione di un oggetto
//+------------------------------------------------------------------+ //| Возвращает описание объекта | //+------------------------------------------------------------------+ string CColumnCaption::Description(void) { return(::StringFormat("%s: Column %u, Value: \"%s\"", TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.Column(),this.Value())); }
Viene creata una stringa di descrizione e restituita nel formato (Tipo di oggetto: Colonna XX, Valore "Valore")
Metodo che restituisce la descrizione dell'oggetto nel registro
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта | //+------------------------------------------------------------------+ void CColumnCaption::Print(void) { ::Print(this.Description()); }
Stampa semplicemente la descrizione dell'intestazione nel registro.
Ora tali oggetti dovrebbero essere inseriti nell'elenco, che costituirà l'intestazione della tabella. Scrivi la classe dell'intestazione della tabella:
//+------------------------------------------------------------------+ //| Класс заголовка таблицы | //+------------------------------------------------------------------+ class CTableHeader : public CObject { protected: CColumnCaption m_caption_tmp; // Объект заголовка столбца для поиска в списке CListObj m_list_captions; // Список заголовков столбцлв //--- Добавляет указанный заголовок в конец списка bool AddNewColumnCaption(CColumnCaption *caption); //--- Создаёт заголовок таблицы из строкового массива void CreateHeader(string &array[]); //--- Устанавливает позицию столбца всем заголовкам столбцов void ColumnPositionUpdate(void); public: //--- Создаёт новый заголовок и добавляет в конец списка CColumnCaption *CreateNewColumnCaption(const string caption); //--- Возвращает (1) заголовок по индексу, (2) количество заголовков столбцов CColumnCaption *GetColumnCaption(const uint index) { return this.m_list_captions.GetNodeAtIndex(index); } uint ColumnsTotal(void) const { return this.m_list_captions.Total(); } //--- Устанавливает значение указанному заголовку столбца void ColumnCaptionSetValue(const uint index,const string value); //--- (1) Устанавливает, (2) возвращает тип данных для указанного заголовка столбца void ColumnCaptionSetDatatype(const uint index,const ENUM_DATATYPE type); ENUM_DATATYPE ColumnCaptionDatatype(const uint index); //--- (1) Удаляет (2) перемещает заголовок столбца bool ColumnCaptionDelete(const uint index); bool ColumnCaptionMoveTo(const uint caption_index, const uint index_to); //--- Очищает данные заголовков столбцов void ClearData(void); //--- Очищает список заголовков столбцов void Destroy(void) { this.m_list_captions.Clear(); } //--- (1) Возвращает, (2) выводит в журнал описание объекта virtual string Description(void); void Print(const bool detail, const bool as_table=false, const int column_width=CELL_WIDTH_IN_CHARS); //--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта virtual int Compare(const CObject *node,const int mode=0) const; virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); virtual int Type(void) const { return(OBJECT_TYPE_TABLE_HEADER); } //--- Конструкторы/деструктор CTableHeader(void) {} CTableHeader(string &array[]) { this.CreateHeader(array); } ~CTableHeader(void){} };
Consideriamo i metodi della classe.
Un metodo che crea una nuova intestazione e la aggiunge alla fine dell'elenco delle intestazioni di colonna
//+------------------------------------------------------------------+ //| Создаёт новый заголовок и добавляет в конец списка | //+------------------------------------------------------------------+ CColumnCaption *CTableHeader::CreateNewColumnCaption(const string caption) { //--- Создаём новый объект заголовка CColumnCaption *caption_obj=new CColumnCaption(this.ColumnsTotal(),caption); if(caption_obj==NULL) { ::PrintFormat("%s: Error. Failed to create new column caption at position %u",__FUNCTION__, this.ColumnsTotal()); return NULL; } //--- Добавляем созданный заголовок в конец списка if(!this.AddNewColumnCaption(caption_obj)) { delete caption_obj; return NULL; } //--- Возвращаем указатель на объект return caption_obj; }
Il testo dell'intestazione viene passato al metodo. Viene creato un nuovo oggetto intestazione di colonna con il testo specificato e un indice pari al numero di intestazioni nell'elenco. Questo sarà l'indice dell'ultima intestazione. Successivamente, l'oggetto creato viene posizionato alla fine dell'elenco delle intestazioni di colonna e viene restituito un puntatore all'intestazione creata.
Un metodo che aggiunge l'intestazione specificata alla fine dell'elenco
//+------------------------------------------------------------------+ //| Добавляет заголовок в конец списка | //+------------------------------------------------------------------+ bool CTableHeader::AddNewColumnCaption(CColumnCaption *caption) { //--- Если передан пустой объект - сообщаем и возвращаем false if(caption==NULL) { ::PrintFormat("%s: Error. Empty CColumnCaption object passed",__FUNCTION__); return false; } //--- Устанавливаем индекс заголовка в списке и добавляем созданный заголовок в конец списка caption.SetColumn(this.ColumnsTotal()); if(this.m_list_captions.Add(caption)==WRONG_VALUE) { ::PrintFormat("%s: Error. Failed to add caption (%u) to list",__FUNCTION__,this.ColumnsTotal()); return false; } //--- Успешно return true; }
Al metodo viene passato un puntatore all'oggetto dell'intestazione di colonna. Deve essere posizionato alla fine dell'elenco delle intestazioni. Il metodo restituisce il risultato dell'aggiunta di un oggetto all'elenco.
Un metodo che crea un'intestazione di tabella da un array di stringhe
//+------------------------------------------------------------------+ //| Создаёт заголовок таблицы из строкового массива | //+------------------------------------------------------------------+ void CTableHeader::CreateHeader(string &array[]) { //--- Получаем из свойств массива количество столбцов таблицы uint total=array.Size(); //--- В цикле по размеру массива //--- создаём все заголовки, добавляя каждый новый в конец списка for(uint i=0; i<total; i++) this.CreateNewColumnCaption(array[i]); }
Al metodo viene passato un array di testo di intestazioni. La dimensione dell'array determina il numero di oggetti di intestazione di colonna da creare, che vengono creati quando si passano in un ciclo i valori dei testi di intestazione nell'array.
Un metodo che imposta un valore sull'intestazione specificata di una colonna
//+------------------------------------------------------------------+ //| Устанавливает значение в указанный заголовок столбца | //+------------------------------------------------------------------+ void CTableHeader::ColumnCaptionSetValue(const uint index,const string value) { //--- Получаем из списка нужный заголовок и записываем в него новое значение CColumnCaption *caption=this.GetColumnCaption(index); if(caption!=NULL) caption.SetValue(value); }
Questo metodo consente di impostare un nuovo valore di testo come specificato dall'indice dell'intestazione.
Un metodo che imposta un tipo di dati per l'intestazione della colonna specificata
//+------------------------------------------------------------------+ //| Устанавливает тип данных для указанного заголовка столбца | //+------------------------------------------------------------------+ void CTableHeader::ColumnCaptionSetDatatype(const uint index,const ENUM_DATATYPE type) { //--- Получаем из списка нужный заголовок и записываем в него новое значение CColumnCaption *caption=this.GetColumnCaption(index); if(caption!=NULL) caption.SetDatatype(type); }
Questo metodo consente di impostare un nuovo valore dei dati memorizzati nella colonna indicata dall'indice dell'intestazione. Per ogni colonna della tabella è possibile impostare il tipo di dati memorizzati nelle celle della colonna. Impostando un tipo di dati sull'oggetto intestazione è possibile impostare lo stesso valore per l'intera colonna dopo. È possibile leggere il valore dei dati dell'intera colonna leggendo questo valore dall'intestazione di questa colonna.
Un metodo che restituisce un tipo di dati dell'intestazione di colonna specificata
//+------------------------------------------------------------------+ //| Возвращает тип данных указанного заголовка столбца | //+------------------------------------------------------------------+ ENUM_DATATYPE CTableHeader::ColumnCaptionDatatype(const uint index) { //--- Получаем из списка нужный заголовок и возвращаем из него тип данных столбца CColumnCaption *caption=this.GetColumnCaption(index); return(caption!=NULL ? caption.Datatype() : (ENUM_DATATYPE)WRONG_VALUE); }
Questo metodo consente di recuperare un valore dei dati memorizzati nella colonna tramite l'indice dell'intestazione. Ottenere il valore dall'intestazione consente di scoprire il tipo di valori memorizzati in tutte le celle di questa colonna della tabella.
Un metodo che elimina l'intestazione della colonna specificata
//+------------------------------------------------------------------+ //| Удаляет заголовок указанного столбца | //+------------------------------------------------------------------+ bool CTableHeader::ColumnCaptionDelete(const uint index) { //--- Удаляем заголовок в списке по индексу if(!this.m_list_captions.Delete(index)) return false; //--- Обновляем индексы для оставшихся заголовков в списке this.ColumnPositionUpdate(); return true; }
L'oggetto all'indice specificato viene eliminato dall'elenco delle intestazioni. Dopo aver eliminato correttamente l'oggetto intestazione di colonna, è necessario aggiornare gli indici degli oggetti rimanenti nell'elenco.
Un metodo che sposta un'intestazione di colonna nella posizione specificata
//+------------------------------------------------------------------+ //| Перемещает заголовок столбца на указанную позицию | //+------------------------------------------------------------------+ bool CTableHeader::ColumnCaptionMoveTo(const uint caption_index,const uint index_to) { //--- Получаем нужный заголовок по индексу в списке, делая его текущим CColumnCaption *caption=this.GetColumnCaption(caption_index); //--- Перемещаем текущий заголовок на указанную позицию в списке if(caption==NULL || !this.m_list_captions.MoveToIndex(index_to)) return false; //--- Обновляем индексы всех заголовков в списке this.ColumnPositionUpdate(); return true; }
Permette di spostare l'intestazione dall'indice specificato a una nuova posizione nell'elenco.
Un metodo che imposta le posizioni delle colonne per tutte le intestazioni
//+------------------------------------------------------------------+ //| Устанавливает позиции столбца всем заголовкам | //+------------------------------------------------------------------+ void CTableHeader::ColumnPositionUpdate(void) { //--- В цикле по всем заголовкам в списке for(int i=0;i<this.m_list_captions.Total();i++) { //--- получаем очередной заголовок и устанавливаем в него индекс столбца CColumnCaption *caption=this.GetColumnCaption(i); if(caption!=NULL) caption.SetColumn(this.m_list_captions.IndexOf(caption)); } }
Dopo aver eliminato o spostato un oggetto nell'elenco in un'altra posizione, è necessario riassegnare gli indici a tutti gli altri oggetti nell'elenco in modo che i loro indici corrispondano alla posizione effettiva nell'elenco. Il metodo esegue un ciclo attraverso tutti gli oggetti nell'elenco, ottiene l'indice reale di ciascun oggetto e lo imposta come proprietà dell'oggetto.
Un metodo che cancella i dati dell'intestazione di colonna nell'elenco
//+------------------------------------------------------------------+ //| Очищает данные заголовков столбцов в списке | //+------------------------------------------------------------------+ void CTableHeader::ClearData(void) { //--- В цикле по всем заголовкам в списке for(uint i=0;i<this.ColumnsTotal();i++) { //--- получаем очередной заголовок и устанавливаем в него пустое значение CColumnCaption *caption=this.GetColumnCaption(i); if(caption!=NULL) caption.ClearData(); } }
In un ciclo attraverso tutti gli oggetti di intestazione di colonna nell'elenco, ottieni ogni oggetto regolare e imposta il testo dell'intestazione su un valore vuoto. In questo modo vengono cancellate completamente le intestazioni di ogni colonna della tabella.
Un metodo che restituisce la descrizione di un oggetto
//+------------------------------------------------------------------+ //| Возвращает описание объекта | //+------------------------------------------------------------------+ string CTableHeader::Description(void) { return(::StringFormat("%s: Captions total: %u", TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.ColumnsTotal())); }
Viene creata una stringa e restituita nel formato (Tipo di Oggetto: Totale didascalie: XX)
Metodo che restituisce la descrizione dell'oggetto nel registro
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта | //+------------------------------------------------------------------+ void CTableHeader::Print(const bool detail, const bool as_table=false, const int column_width=CELL_WIDTH_IN_CHARS) { //--- Количество заголовков int total=(int)this.ColumnsTotal(); //--- Если вывод в табличном виде string res=""; if(as_table) { //--- создаём строку таблицы из значений всех заголовков res="|"; for(int i=0;i<total;i++) { CColumnCaption *caption=this.GetColumnCaption(i); if(caption==NULL) continue; res+=::StringFormat("%*s |",column_width,caption.Value()); } //--- Выводим строку в журнал и уходим ::Print(res); return; } //--- Выводим заголовок в виде описания строки ::Print(this.Description()+(detail ? ":" : "")); //--- Если детализированное описание if(detail) { //--- В цикле по списку заголовков строки for(int i=0; i<total; i++) { //--- получаем текущий заголовок и добавляем в итоговую строку его описание CColumnCaption *caption=this.GetColumnCaption(i); if(caption!=NULL) res+=" "+caption.Description()+(i<total-1 ? "\n" : ""); } //--- Выводим в журнал созданную в цикле строку ::Print(res); } }
Il metodo può registrare la descrizione dell'intestazione in formato tabellare e come elenco di intestazioni di colonna.
Metodi per salvare in un file e scaricare un'intestazione da un file
//+------------------------------------------------------------------+ //| Сохранение в файл | //+------------------------------------------------------------------+ bool CTableHeader::Save(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Сохраняем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileWriteLong(file_handle,MARKER_START_DATA)!=sizeof(long)) return(false); //--- Сохраняем тип объекта if(::FileWriteInteger(file_handle,this.Type(),INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем список заголовков if(!this.m_list_captions.Save(file_handle)) return(false); //--- Успешно return true; } //+------------------------------------------------------------------+ //| Загрузка из файла | //+------------------------------------------------------------------+ bool CTableHeader::Load(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Загружаем и проверяем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return(false); //--- Загружаем тип объекта if(::FileReadInteger(file_handle,INT_VALUE)!=this.Type()) return(false); //--- Загружаем список заголовков if(!this.m_list_captions.Load(file_handle)) return(false); //--- Успешно return true; }
La logica dei metodi è commentata nel codice e non differisce in alcun modo da metodi simili di altre classi già create per la creazione di tabelle.
Abbiamo tutto pronto per iniziare ad assemblare le classi della tabella. La classe della tabella dovrebbe essere in grado di creare una tabella basata sul suo modello e dovrebbe avere un'intestazione in base alla quale verranno denominate le colonne della tabella. Se non si specifica l'intestazione della tabella nella tabella stessa, questa verrà creata solo in base al modello, sarà statica e le sue funzionalità saranno limitate solo alla visualizzazione della tabella. Per le tabelle semplici, questo è più che sufficiente. Tuttavia, per poter interagire con l'utente tramite il componente Controller, è necessario determinare l'intestazione della tabella nella tabella stessa. Ciò fornirà un'ampia gamma di opzioni per il controllo delle tabelle e dei relativi dati. Ma faremo tutto più tardi. Diamo ora un'occhiata alle classi delle tabelle.
Classi della tabella
Continua a scrivere il codice nello stesso file e implementa la classe della tabella:
//+------------------------------------------------------------------+ //| Класс таблицы | //+------------------------------------------------------------------+ class CTable : public CObject { private: //--- Заполняет массив заголовков столбцов в стиле Excel bool FillArrayExcelNames(const uint num_columns); //--- Возвращает наименование столбца как в Excel string GetExcelColumnName(uint column_number); //--- Возвращает доступность заголовка bool HeaderCheck(void) const { return(this.m_table_header!=NULL && this.m_table_header.ColumnsTotal()>0); } protected: CTableModel *m_table_model; // Указатель на модель таблицы CTableHeader *m_table_header; // Указатель на заголовок таблицы CList m_list_rows; // Список массивов параметров из полей структуры string m_array_names[]; // Массив заголовков столбцов int m_id; // Идентификатор таблицы //--- Копирует массив наименований заголовков bool ArrayNamesCopy(const string &column_names[],const uint columns_total); public: //--- (1) Устанавливает, (2) возвращает модель таблицы void SetTableModel(CTableModel *table_model) { this.m_table_model=table_model; } CTableModel *GetTableModel(void) { return this.m_table_model; } //--- (1) Устанавливает, (2) возвращает заголовок void SetTableHeader(CTableHeader *table_header) { this.m_table_header=m_table_header; } CTableHeader *GetTableHeader(void) { return this.m_table_header; } //--- (1) Устанавливает, (2) возвращает идентификатор таблицы void SetID(const int id) { this.m_id=id; } int ID(void) const { return this.m_id; } //--- Очищает данные заголовков столбцов void HeaderClearData(void) { if(this.m_table_header!=NULL) this.m_table_header.ClearData(); } //--- Удаляет заголовок таблицы void HeaderDestroy(void) { if(this.m_table_header==NULL) return; this.m_table_header.Destroy(); this.m_table_header=NULL; } //--- (1) Очищает все данные, (2) уничтожает модель таблицы и заголовок void ClearData(void) { if(this.m_table_model!=NULL) this.m_table_model.ClearData(); } void Destroy(void) { if(this.m_table_model==NULL) return; this.m_table_model.Destroy(); this.m_table_model=NULL; } //--- Возвращает (1) заголовок, (2) ячейку, (3) строку по индексу, количество (4) строк, (5) столбцов, ячеек (6) в указанной строке, (7) в таблице CColumnCaption *GetColumnCaption(const uint index) { return(this.m_table_header!=NULL ? this.m_table_header.GetColumnCaption(index) : NULL); } CTableCell *GetCell(const uint row, const uint col) { return(this.m_table_model!=NULL ? this.m_table_model.GetCell(row,col) : NULL); } CTableRow *GetRow(const uint index) { return(this.m_table_model!=NULL ? this.m_table_model.GetRow(index) : NULL); } uint RowsTotal(void) const { return(this.m_table_model!=NULL ? this.m_table_model.RowsTotal() : 0); } uint ColumnsTotal(void) const { return(this.m_table_model!=NULL ? this.m_table_model.CellsInRow(0) : 0); } uint CellsInRow(const uint index) { return(this.m_table_model!=NULL ? this.m_table_model.CellsInRow(index) : 0); } uint CellsTotal(void) { return(this.m_table_model!=NULL ? this.m_table_model.CellsTotal() : 0); } //--- Устанавливает (1) значение, (2) точность, (3) флаги отображения времени, (4) флаг отображения имён цветов в указанную ячейку template<typename T> void CellSetValue(const uint row, const uint col, const T value); void CellSetDigits(const uint row, const uint col, const int digits); void CellSetTimeFlags(const uint row, const uint col, const uint flags); void CellSetColorNamesFlag(const uint row, const uint col, const bool flag); //--- (1) Назначает, (2) отменяет объект в ячейке void CellAssignObject(const uint row, const uint col,CObject *object); void CellUnassignObject(const uint row, const uint col); //--- Возвращает строковое значение указанной ячейки virtual string CellValueAt(const uint row, const uint col); protected: //--- (1) Удаляет (2) перемещает ячейку bool CellDelete(const uint row, const uint col); bool CellMoveTo(const uint row, const uint cell_index, const uint index_to); public: //--- (1) Возвращает, (2) выводит в журнал описание ячейки, (3) назначенный в ячейку объект string CellDescription(const uint row, const uint col); void CellPrint(const uint row, const uint col); //---Возвращает (1) назначенный в ячейку объект, (2) тип назначенного в ячейку объекта CObject *CellGetObject(const uint row, const uint col); ENUM_OBJECT_TYPE CellGetObjType(const uint row, const uint col); //--- Создаёт новую строку и (1) добавляет в конец списка, (2) вставляет в указанную позицию списка CTableRow *RowAddNew(void); CTableRow *RowInsertNewTo(const uint index_to); //--- (1) Удаляет (2) перемещает строку, (3) очищает данные строки bool RowDelete(const uint index); bool RowMoveTo(const uint row_index, const uint index_to); void RowClearData(const uint index); //--- (1) Возвращает, (2) выводит в журнал описание строки string RowDescription(const uint index); void RowPrint(const uint index,const bool detail); //--- (1) Добавляет новый, (2) удаляет, (3) перемещает столбец, (4) очищает данные столбца bool ColumnAddNew(const string caption,const int index=-1); bool ColumnDelete(const uint index); bool ColumnMoveTo(const uint index, const uint index_to); void ColumnClearData(const uint index); //--- Устанавливает (1) значение указанному заголовку, (2) точность данных указанному столбцу void ColumnCaptionSetValue(const uint index,const string value); void ColumnSetDigits(const uint index,const int digits); //--- (1) Устанавливает, (2) возвращает тип данных для указанного столбца void ColumnSetDatatype(const uint index,const ENUM_DATATYPE type); ENUM_DATATYPE ColumnDatatype(const uint index); //--- (1) Возвращает, (2) выводит в журнал описание объекта virtual string Description(void); void Print(const int column_width=CELL_WIDTH_IN_CHARS); //--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта virtual int Compare(const CObject *node,const int mode=0) const; virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); virtual int Type(void) const { return(OBJECT_TYPE_TABLE); } //--- Конструкторы/деструктор CTable(void) : m_table_model(NULL), m_table_header(NULL) { this.m_list_rows.Clear();} template<typename T> CTable(T &row_data[][],const string &column_names[]); CTable(const uint num_rows, const uint num_columns); CTable(const matrix &row_data,const string &column_names[]); ~CTable (void); };
Nella classe vengono dichiarati i puntatori all'intestazione e al modello della tabella. Per creare una tabella, è necessario prima creare un modello di tabella dai dati passati ai costruttori di classe. La tabella può generare automaticamente un'intestazione con i nomi delle colonne nello stile di MS Excel, dove a ogni colonna viene assegnato un nome composto da lettere latine.
L'algoritmo per il calcolo dei nomi è il seguente:
-
Nomi composti da una sola lettera - le prime 26 colonne sono indicate dalle lettere dalla "A" alla "Z".
-
Nomi di due lettere - dopo la "Z", le colonne sono designate da una combinazione di due lettere. La prima lettera cambia più lentamente, mentre la seconda itera l'intero alfabeto. Per esempio:
- "AA", "AB", "AC", ..., "AZ",
- poi "BA", "BB", ..., "BZ",
- ecc.
-
Nomi di tre lettere - dopo "ZZ", le colonne sono designate da una combinazione di tre lettere. Il principio è lo stesso:
- "AAA", "AAB", ..., "AAZ",
- poi "ABA", "ABB", ..., "ABZ",
- ecc.
-
Il principio generale è che i nomi delle colonne possono essere considerati numeri nel sistema numerico con base 26, dove "A" corrisponde a 1, "B" corrisponde a 2, ..., "Z" — 26. Per esempio:
- "A" = 1,
- "Z" = 26,
- "AA" = 27 (1 * 26^1 + 1),
- "AB" = 28 (1 * 26^1 + 2),
- "BA" = 53 (2 * 26^1 + 1).
Pertanto, l'algoritmo genera automaticamente i nomi delle colonne, incrementandoli in base al principio considerato. Il numero massimo di colonne in Excel dipende dalla versione del programma (ad esempio, in Excel 2007 e versioni successive sono 16.384 e terminano con "XFD"). L'algoritmo creato qui non si limita a questa cifra. Può assegnare nomi al numero di colonne pari a INT_MAX :
//+------------------------------------------------------------------+ //| Возвращает наименование столбца как в Excel | //+------------------------------------------------------------------+ string CTable::GetExcelColumnName(uint column_number) { string column_name=""; uint index=column_number; //--- Проверяем, что номер столбца больше 0 if(index==0) return (__FUNCTION__+": Error. Invalid column number passed"); //--- Преобразование номера в название столбца while(!::IsStopped() && index>0) { index--; // Уменьшаем номер на 1, чтобы сделать его 0-индексным uint remainder =index % 26; // Остаток от деления на 26 uchar char_code ='A'+(uchar)remainder; // Рассчитываем код символа (буквы) column_name=::CharToString(char_code)+column_name; // Добавляем букву в начало строки index/=26; // Переходим к следующему разряду } return column_name; } //+------------------------------------------------------------------+ //| Заполняет массив заголовков столбцов в стиле Excel | //+------------------------------------------------------------------+ bool CTable::FillArrayExcelNames(const uint num_columns) { ::ResetLastError(); if(::ArrayResize(this.m_array_names,num_columns,num_columns)!=num_columns) { ::PrintFormat("%s: ArrayResize() failed. Error %d",__FUNCTION__,::GetLastError()); return false; } for(int i=0;i<(int)num_columns;i++) this.m_array_names[i]=this.GetExcelColumnName(i+1); return true; }
I metodi consentono di riempire un array con i nomi delle colonne in stile Excel.
Consideriamo i costruttori parametrici di una classe.
Un costruttore di modello che specifica un array bidimensionale di dati e un array di stringhe di intestazioni
//+-------------------------------------------------------------------+ //| Конструктор с указанием массива таблицы и массива заголовков. | //| Определяет количество и наименования колонок согласно column_names| //| Количество строк определены размером массива данных row_data, | //| который используется и для заполнения таблицы | //+-------------------------------------------------------------------+ template<typename T> CTable::CTable(T &row_data[][],const string &column_names[]) : m_id(-1) { this.m_table_model=new CTableModel(row_data); if(column_names.Size()>0) this.ArrayNamesCopy(column_names,row_data.Range(1)); else { ::PrintFormat("%s: An empty array names was passed. The header array will be filled in Excel style (A, B, C)",__FUNCTION__); this.FillArrayExcelNames((uint)::ArrayRange(row_data,1)); } this.m_table_header=new CTableHeader(this.m_array_names); }
Un array di dati di qualsiasi tipo viene passato al costruttore del modello dall'enumerazione ENUM_DATATYPE. Successivamente, verrà convertito nel tipo di dati utilizzato dalle tabelle (double, long, datetime, color, string) per creare un modello di tabella e un array di intestazioni di colonna. Se l’array delle intestazioni è vuoto, verranno create intestazioni in stile MS Excel.
Costruttore con indicazione del numero di righe e colonne della tabella
//+------------------------------------------------------------------+ //| Конструктор таблицы с определением количества колонок и строк. | //| Колонки будут иметь Excel-наименования "A", "B", "C" и т.д. | //+------------------------------------------------------------------+ CTable::CTable(const uint num_rows,const uint num_columns) : m_table_header(NULL), m_id(-1) { this.m_table_model=new CTableModel(num_rows,num_columns); if(this.FillArrayExcelNames(num_columns)) this.m_table_header=new CTableHeader(this.m_array_names); }
Il costruttore crea un modello di tabella vuoto con un'intestazione in stile MS Excel.
Il costruttore basato su una matrice di dati e un array di intestazioni di colonna
//+-------------------------------------------------------------------+ //| Конструктор таблицы с инициализацией колонок согласно column_names| //| Количество строк определены параметром row_data, с типом matrix | //+-------------------------------------------------------------------+ CTable::CTable(const matrix &row_data,const string &column_names[]) : m_id(-1) { this.m_table_model=new CTableModel(row_data); if(column_names.Size()>0) this.ArrayNamesCopy(column_names,(uint)row_data.Cols()); else { ::PrintFormat("%s: An empty array names was passed. The header array will be filled in Excel style (A, B, C)",__FUNCTION__); this.FillArrayExcelNames((uint)row_data.Cols()); } this.m_table_header=new CTableHeader(this.m_array_names); }
Una matrice di dati di tipo double viene passata al costruttore per creare un modello di tabella e un array di intestazioni di colonna. Se l’array delle intestazioni è vuoto, verranno create intestazioni in stile MS Excel.
Nel distruttore di classe vengono distrutti il modello e l'intestazione della tabella.
//+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CTable::~CTable(void) { if(this.m_table_model!=NULL) { this.m_table_model.Destroy(); delete this.m_table_model; } if(this.m_table_header!=NULL) { this.m_table_header.Destroy(); delete this.m_table_header; } }
Un metodo per confrontare due oggetti
//+------------------------------------------------------------------+ //| Сравнение двух объектов | //+------------------------------------------------------------------+ int CTable::Compare(const CObject *node,const int mode=0) const { const CTable *obj=node; return(this.ID()>obj.ID() ? 1 : this.ID()<obj.ID() ? -1 : 0); }
Se il programma deve creare più tabelle, è possibile assegnare un identificatore a ciascuna tabella. Le tabelle nel programma possono essere identificate tramite l'identificatore del set, che per impostazione predefinita ha il valore -1. Se le tabelle create vengono inserite in elenchi ( CList, CArrayObj, ecc.), il metodo di confronto consente di confrontare le tabelle in base ai loro identificatori per cercarle e ordinarle:
//+------------------------------------------------------------------+ //| Сравнение двух объектов | //+------------------------------------------------------------------+ int CTable::Compare(const CObject *node,const int mode=0) const { const CTable *obj=node; return(this.ID()>obj.ID() ? 1 : this.ID()<obj.ID() ? -1 : 0); }
Un metodo che copia un array di nomi di intestazione
//+------------------------------------------------------------------+ //| Копирует массив наименований заголовков | //+------------------------------------------------------------------+ bool CTable::ArrayNamesCopy(const string &column_names[],const uint columns_total) { if(columns_total==0) { ::PrintFormat("%s: Error. The table has no columns",__FUNCTION__); return false; } if(columns_total>column_names.Size()) { ::PrintFormat("%s: The number of header names is less than the number of columns. The header array will be filled in Excel style (A, B, C)",__FUNCTION__); return this.FillArrayExcelNames(columns_total); } uint total=::fmin(columns_total,column_names.Size()); return(::ArrayCopy(this.m_array_names,column_names,0,0,total)==total); }
Al metodo viene passato un array di intestazioni e il numero di colonne nel modello di tabella creato. Se nella tabella non ci sono colonne, non è necessario creare intestazioni. Segnalalo e restituisci false. Se nel modello di tabella sono presenti più colonne rispetto alle intestazioni nell'array passato, tutte le intestazioni verranno create nello stile Excel, in modo che la tabella non abbia colonne senza didascalie nelle intestazioni.
Un metodo che imposta un valore sulla cella specificata
//+------------------------------------------------------------------+ //| Устанавливает значение в указанную ячейку | //+------------------------------------------------------------------+ template<typename T> void CTable::CellSetValue(const uint row, const uint col, const T value) { if(this.m_table_model!=NULL) this.m_table_model.CellSetValue(row,col,value); }
Qui ci riferiamo al metodo con lo stesso nome per l'oggetto modello di tabella.
in sostanza, in questa classe, molti metodi vengono duplicati dalla classe del modello di tabella. Se il modello viene creato, poi viene chiamato il metodo simile per ottenere o impostare la proprietà.
Un metodo di gestione delle celle della tabella
//+------------------------------------------------------------------+ //| Устанавливает точность в указанную ячейку | //+------------------------------------------------------------------+ void CTable::CellSetDigits(const uint row, const uint col, const int digits) { if(this.m_table_model!=NULL) this.m_table_model.CellSetDigits(row,col,digits); } //+------------------------------------------------------------------+ //| Устанавливает флаги отображения времени в указанную ячейку | //+------------------------------------------------------------------+ void CTable::CellSetTimeFlags(const uint row, const uint col, const uint flags) { if(this.m_table_model!=NULL) this.m_table_model.CellSetTimeFlags(row,col,flags); } //+------------------------------------------------------------------+ //| Устанавливает флаг отображения имён цветов в указанную ячейку | //+------------------------------------------------------------------+ void CTable::CellSetColorNamesFlag(const uint row, const uint col, const bool flag) { if(this.m_table_model!=NULL) this.m_table_model.CellSetColorNamesFlag(row,col,flag); } //+------------------------------------------------------------------+ //| Назначает объект в ячейку | //+------------------------------------------------------------------+ void CTable::CellAssignObject(const uint row, const uint col,CObject *object) { if(this.m_table_model!=NULL) this.m_table_model.CellAssignObject(row,col,object); } //+------------------------------------------------------------------+ //| Отменяет объект в ячейке | //+------------------------------------------------------------------+ void CTable::CellUnassignObject(const uint row, const uint col) { if(this.m_table_model!=NULL) this.m_table_model.CellUnassignObject(row,col); } //+------------------------------------------------------------------+ //| Возвращает строковое значение указанной ячейки | //+------------------------------------------------------------------+ string CTable::CellValueAt(const uint row,const uint col) { CTableCell *cell=this.GetCell(row,col); return(cell!=NULL ? cell.Value() : ""); } //+------------------------------------------------------------------+ //| Удаляет ячейку | //+------------------------------------------------------------------+ bool CTable::CellDelete(const uint row, const uint col) { return(this.m_table_model!=NULL ? this.m_table_model.CellDelete(row,col) : false); } //+------------------------------------------------------------------+ //| Перемещает ячейку | //+------------------------------------------------------------------+ bool CTable::CellMoveTo(const uint row, const uint cell_index, const uint index_to) { return(this.m_table_model!=NULL ? this.m_table_model.CellMoveTo(row,cell_index,index_to) : false); } //+------------------------------------------------------------------+ //| Возвращает назначенный в ячейку объект | //+------------------------------------------------------------------+ CObject *CTable::CellGetObject(const uint row, const uint col) { return(this.m_table_model!=NULL ? this.m_table_model.CellGetObject(row,col) : NULL); } //+------------------------------------------------------------------+ //| Возвращает тип назначенного в ячейку объекта | //+------------------------------------------------------------------+ ENUM_OBJECT_TYPE CTable::CellGetObjType(const uint row,const uint col) { return(this.m_table_model!=NULL ? this.m_table_model.CellGetObjType(row,col) : (ENUM_OBJECT_TYPE)WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает описание ячейки | //+------------------------------------------------------------------+ string CTable::CellDescription(const uint row, const uint col) { return(this.m_table_model!=NULL ? this.m_table_model.CellDescription(row,col) : ""); } //+------------------------------------------------------------------+ //| Выводит в журнал описание ячейки | //+------------------------------------------------------------------+ void CTable::CellPrint(const uint row, const uint col) { if(this.m_table_model!=NULL) this.m_table_model.CellPrint(row,col); }
Metodi per lavorare con le righe della tabella
//+------------------------------------------------------------------+ //| Создаёт новую строку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableRow *CTable::RowAddNew(void) { return(this.m_table_model!=NULL ? this.m_table_model.RowAddNew() : NULL); } //+------------------------------------------------------------------+ //| Создаёт новую строку и вставляет в указанную позицию списка | //+------------------------------------------------------------------+ CTableRow *CTable::RowInsertNewTo(const uint index_to) { return(this.m_table_model!=NULL ? this.m_table_model.RowInsertNewTo(index_to) : NULL); } //+------------------------------------------------------------------+ //| Удаляет строку | //+------------------------------------------------------------------+ bool CTable::RowDelete(const uint index) { return(this.m_table_model!=NULL ? this.m_table_model.RowDelete(index) : false); } //+------------------------------------------------------------------+ //| Перемещает строку | //+------------------------------------------------------------------+ bool CTable::RowMoveTo(const uint row_index, const uint index_to) { return(this.m_table_model!=NULL ? this.m_table_model.RowMoveTo(row_index,index_to) : false); } //+------------------------------------------------------------------+ //| Очищает данные строки | //+------------------------------------------------------------------+ void CTable::RowClearData(const uint index) { if(this.m_table_model!=NULL) this.m_table_model.RowClearData(index); } //+------------------------------------------------------------------+ //| Возвращает описание строки | //+------------------------------------------------------------------+ string CTable::RowDescription(const uint index) { return(this.m_table_model!=NULL ? this.m_table_model.RowDescription(index) : ""); } //+------------------------------------------------------------------+ //| Выводит в журнал описание строки | //+------------------------------------------------------------------+ void CTable::RowPrint(const uint index,const bool detail) { if(this.m_table_model!=NULL) this.m_table_model.RowPrint(index,detail); }
Un metodo che crea una nuova colonna e la aggiunge alla posizione della tabella specificata
//+------------------------------------------------------------------+ //| Создаёт новый столбец и добавляет его в указанную позицию таблицы| //+------------------------------------------------------------------+ bool CTable::ColumnAddNew(const string caption,const int index=-1) { //--- Если нет модели таблицы, либо ошибка добавления нового столбца к модели - возвращаем false if(this.m_table_model==NULL || !this.m_table_model.ColumnAddNew(index)) return false; //--- Если нет заголовка - возвращаем true (столбец добавлен без заголовка) if(this.m_table_header==NULL) return true; //--- Проверяем создание нового заголовка столбца и, если не создан - возвращаем false CColumnCaption *caption_obj=this.m_table_header.CreateNewColumnCaption(caption); if(caption_obj==NULL) return false; //--- Если передан не отрицательный индекс - возвращаем результат перемещения заголовка на указанный индекс //--- В ином случае уже всё готово - просто возвращаем true return(index>-1 ? this.m_table_header.ColumnCaptionMoveTo(caption_obj.Column(),index) : true); }
Se non è presente alcun modello di tabella, il metodo restituisce immediatamente un errore. Se la colonna è stata aggiunta correttamente al modello di tabella, provare ad aggiungere l'intestazione appropriata. Se la tabella non ha un'intestazione, restituisce il successo della creazione di una nuova colonna. Se è presente un'intestazione, aggiunge una nuova intestazione di colonna e la sposta nella posizione specificata nell'elenco.
Altri metodi per lavorare con le colonne
//+------------------------------------------------------------------+ //| Удаляет столбец | //+------------------------------------------------------------------+ bool CTable::ColumnDelete(const uint index) { if(!this.HeaderCheck() || !this.m_table_header.ColumnCaptionDelete(index)) return false; return this.m_table_model.ColumnDelete(index); } //+------------------------------------------------------------------+ //| Перемещает столбец | //+------------------------------------------------------------------+ bool CTable::ColumnMoveTo(const uint index, const uint index_to) { if(!this.HeaderCheck() || !this.m_table_header.ColumnCaptionMoveTo(index,index_to)) return false; return this.m_table_model.ColumnMoveTo(index,index_to); } //+------------------------------------------------------------------+ //| Очищает данные столбца | //+------------------------------------------------------------------+ void CTable::ColumnClearData(const uint index) { if(this.m_table_model!=NULL) this.m_table_model.ColumnClearData(index); } //+------------------------------------------------------------------+ //| Устанавливает значение указанному заголовку | //+------------------------------------------------------------------+ void CTable::ColumnCaptionSetValue(const uint index,const string value) { CColumnCaption *caption=this.m_table_header.GetColumnCaption(index); if(caption!=NULL) caption.SetValue(value); } //+------------------------------------------------------------------+ //| Устанавливает тип данных для указанного столбца | //+------------------------------------------------------------------+ void CTable::ColumnSetDatatype(const uint index,const ENUM_DATATYPE type) { //--- Если модель таблицы есть - устанавливаем тип данных для столбца if(this.m_table_model!=NULL) this.m_table_model.ColumnSetDatatype(index,type); //--- Если заголовок есть - устанавливаем тип данных для заголовка if(this.m_table_header!=NULL) this.m_table_header.ColumnCaptionSetDatatype(index,type); } //+------------------------------------------------------------------+ //| Устанавливает точность данных указанному столбцу | //+------------------------------------------------------------------+ void CTable::ColumnSetDigits(const uint index,const int digits) { if(this.m_table_model!=NULL) this.m_table_model.ColumnSetDigits(index,digits); } //+------------------------------------------------------------------+ //| Возвращает тип данных для указанного столбца | //+------------------------------------------------------------------+ ENUM_DATATYPE CTable::ColumnDatatype(const uint index) { return(this.m_table_header!=NULL ? this.m_table_header.ColumnCaptionDatatype(index) : (ENUM_DATATYPE)WRONG_VALUE); }
Un metodo che restituisce la descrizione dell'oggetto
//+------------------------------------------------------------------+ //| Возвращает описание объекта | //+------------------------------------------------------------------+ string CTable::Description(void) { return(::StringFormat("%s: Rows total: %u, Columns total: %u", TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.RowsTotal(),this.ColumnsTotal())); }
Crea e restituisce la stringa nel formato (Tipo di Oggetto: Righe totali: XX, Colonne totali: XX)
Metodo che restituisce la descrizione dell'oggetto nel registro
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта | //+------------------------------------------------------------------+ void CTable::Print(const int column_width=CELL_WIDTH_IN_CHARS) { if(this.HeaderCheck()) { //--- Выводим заголовок в виде описания строки ::Print(this.Description()+":"); //--- Количество заголовков int total=(int)this.ColumnsTotal(); string res=""; //--- создаём строку из значений всех заголовков столбцов таблицы res="|"; for(int i=0;i<total;i++) { CColumnCaption *caption=this.GetColumnCaption(i); if(caption==NULL) continue; res+=::StringFormat("%*s |",column_width,caption.Value()); } //--- Дополняем строку слева заголовком string hd="|"; hd+=::StringFormat("%*s ",column_width,"n/n"); res=hd+res; //--- Выводим строку заголовка в журнал ::Print(res); } //--- Пройдём в цикле по всем строкам таблицы и распечатаем их в табличном виде for(uint i=0;i<this.RowsTotal();i++) { CTableRow *row=this.GetRow(i); if(row!=NULL) { //--- создаём строку таблицы из значений всех ячеек string head=" "+(string)row.Index(); string res=::StringFormat("|%-*s |",column_width,head); for(int i=0;i<(int)row.CellsTotal();i++) { CTableCell *cell=row.GetCell(i); if(cell==NULL) continue; res+=::StringFormat("%*s |",column_width,cell.Value()); } //--- Выводим строку в журнал ::Print(res); } } }
Il metodo invia una descrizione al registro e di seguito è riportata una tabella con l'intestazione e i dati.
Un metodo per salvare la tabella in un file
//+------------------------------------------------------------------+ //| Сохранение в файл | //+------------------------------------------------------------------+ bool CTable::Save(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Сохраняем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileWriteLong(file_handle,MARKER_START_DATA)!=sizeof(long)) return(false); //--- Сохраняем тип объекта if(::FileWriteInteger(file_handle,this.Type(),INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем идентификатор if(::FileWriteInteger(file_handle,this.m_id,INT_VALUE)!=INT_VALUE) return(false); //--- Проверяем модель таблицы if(this.m_table_model==NULL) return false; //--- Сохраняем модель таблицы if(!this.m_table_model.Save(file_handle)) return(false); //--- Проверяем заголовок таблицы if(this.m_table_header==NULL) return false; //--- Сохраняем заголовок таблицы if(!this.m_table_header.Save(file_handle)) return(false); //--- Успешно return true; }
Il salvataggio avrà successo solo se saranno stati creati sia il modello della tabella sia la sua intestazione. L'intestazione può essere vuota, ovvero può non avere colonne, ma l'oggetto deve essere creato.
Metodo di caricamento della tabella da un file
//+------------------------------------------------------------------+ //| Загрузка из файла | //+------------------------------------------------------------------+ bool CTable::Load(const int file_handle) { //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Загружаем и проверяем маркер начала данных - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return(false); //--- Загружаем тип объекта if(::FileReadInteger(file_handle,INT_VALUE)!=this.Type()) return(false); //--- Загружаем идентификатор this.m_id=::FileReadInteger(file_handle,INT_VALUE); //--- Проверяем модель таблицы if(this.m_table_model==NULL && (this.m_table_model=new CTableModel())==NULL) return(false); //--- Загружаем модель таблицы if(!this.m_table_model.Load(file_handle)) return(false); //--- Проверяем заголовок таблицы if(this.m_table_header==NULL && (this.m_table_header=new CTableHeader())==NULL) return false; //--- Загружаем заголовок таблицы if(!this.m_table_header.Load(file_handle)) return(false); //--- Успешно return true; }
Poiché la tabella memorizza sia i dati del modello sia i dati dell'intestazione, con questo metodo (se il modello o l'intestazione non vengono creati nella tabella) vengono pre-creati e in seguito, i relativi dati vengono caricati dal file.
La classe tabella semplice è pronta.
Ora, prendiamo in considerazione l'opzione di ereditare da una semplice classe di tabella - creiamo una classe di tabella che si basa sui dati registrati in CList :
//+------------------------------------------------------------------+ //| Класс для создания таблиц на основе массива параметров | //+------------------------------------------------------------------+ class CTableByParam : public CTable { public: virtual int Type(void) const { return(OBJECT_TYPE_TABLE_BY_PARAM); } //--- Конструктор/деструктор CTableByParam(void) { this.m_list_rows.Clear(); } CTableByParam(CList &row_data,const string &column_names[]); ~CTableByParam(void) {} };
Qui, il tipo di tabella viene restituito come OBJECT_TYPE_TABLE_BY_PARAM e il modello di tabella e l'intestazione vengono creati nel costruttore della classe:
//+------------------------------------------------------------------+ //| Конструктор с указанием массива таблицы на основе списка row_data| //| содержащего объекты с данными полей структуры. | //| Определяет количество и наименования колонок согласно количеству | //| наименований столбцов в массиве column_names | //+------------------------------------------------------------------+ CTableByParam::CTableByParam(CList &row_data,const string &column_names[]) { //--- Копируем переданный список данных в переменную и //--- создаём на основе этого списка модель таблицы this.m_list_rows=row_data; this.m_table_model=new CTableModel(this.m_list_rows); //--- Копируем переданный список заголовков в m_array_names и //--- создаём на основе этого списка заголовок таблицы this.ArrayNamesCopy(column_names,column_names.Size()); this.m_table_header=new CTableHeader(this.m_array_names); }
Sulla base di questo esempio, si possono creare altre classi di tabelle, ma si presuppone che tutto ciò che è stato creato oggi sia più che sufficiente per creare un'ampia varietà di tabelle e un vasto set di possibili dati.
Proviamo tutto ciò che abbiamo.
Verifica del Risultato.
Nella cartella \MQL5\Scripts\Tables\, creare un nuovo script denominato TestEmptyTable.mq5, collega ad esso il file della classe della tabella creata e crea una tabella 4x4 vuota:
//+------------------------------------------------------------------+ //| TestEmptyTable.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые библиотеки | //+------------------------------------------------------------------+ #include "Tables.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Создаём пустую таблицу 4x4 CTable *table=new CTable(4,4); if(table==NULL) return; //--- Распечатываем её в журнале и удаляем созданный объект table.Print(10); delete table; }
Lo script produrrà il seguente output nel registro:
Table: Rows total: 4, Columns total: 4: | n/n | A | B | C | D | | 0 | | | | | | 1 | | | | | | 2 | | | | | | 3 | | | | |
Qui le intestazioni delle colonne vengono create automaticamente nello stile MS Excel.
Scrivi un altro script \MQL5\Scripts\Tables\TestTArrayTable.mq5:
//+------------------------------------------------------------------+ //| TestTArrayTable.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые библиотеки | //+------------------------------------------------------------------+ #include "Tables.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Объявляем и инициализируем double-массив 4x4 double array[4][4]={{ 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12}, {13, 14, 15, 16}}; //--- Объявляем и инициализируем массив заголовков столбцов string headers[]={"Column 1","Column 2","Column 3","Column 4"}; //--- Создаём таблицу на основе массива данных и массива заголовков CTable *table=new CTable(array,headers); if(table==NULL) return; //--- Распечатываем таблицу в журнале и удаляем созданный объект table.Print(10); delete table; }
Come risultato del funzionamento dello script verrà visualizzata la seguente tabella nel registro:
Table: Rows total: 4, Columns total: 4: | n/n | Column 1 | Column 2 | Column 3 | Column 4 | | 0 | 1.00 | 2.00 | 3.00 | 4.00 | | 1 | 5.00 | 6.00 | 7.00 | 8.00 | | 2 | 9.00 | 10.00 | 11.00 | 12.00 | | 3 | 13.00 | 14.00 | 15.00 | 16.00 |
Qui, le colonne sono già intitolate dall'array di intestazioni passato al costruttore della classe. Un array bidimensionale che rappresenta i dati per la creazione di una tabella può essere di qualsiasi tipo dall'enumerazione ENUM_DATATYPE.
Tutti i tipi vengono convertiti automaticamente nei cinque tipi utilizzati nella classe del modello di tabella: double, long, datetime, color e string.
Scrivere lo script \MQL5\Scripts\Tables\TestMatrixTable.mq5 per testare la tabella sui dati della matrice:
//+------------------------------------------------------------------+ //| TestMatrixTable.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые библиотеки | //+------------------------------------------------------------------+ #include "Tables.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Объявляем и инициализируем матрицу 4x4 matrix row_data = {{ 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12}, {13, 14, 15, 16}}; //--- Объявляем и инициализируем массив заголовков столбцов string headers[]={"Column 1","Column 2","Column 3","Column 4"}; //--- Создаём таблицу на основе матрицы и массива заголовков CTable *table=new CTable(row_data,headers); if(table==NULL) return; //--- Распечатываем таблицу в журнале и удаляем созданный объект table.Print(10); delete table; }
Il risultato sarà una tabella simile a quella costruita sulla base di un array bidimensionale 4x4:
Table: Rows total: 4, Columns total: 4: | n/n | Column 1 | Column 2 | Column 3 | Column 4 | | 0 | 1.00 | 2.00 | 3.00 | 4.00 | | 1 | 5.00 | 6.00 | 7.00 | 8.00 | | 2 | 9.00 | 10.00 | 11.00 | 12.00 | | 3 | 13.00 | 14.00 | 15.00 | 16.00 |
Ora, scriviamo lo script \MQL5\Scripts\Tables\TestDealsTable.mq5, in cui contiamo tutta la cronologia dei trade, creiamo una tabella delle negoziazioni basata su di essa e la stampiamo nel registro:
//+------------------------------------------------------------------+ //| TestDealsTable.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые библиотеки | //+------------------------------------------------------------------+ #include "Tables.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Объявляем список сделок, объект параметров сделки и структуру параметров CList rows_data; CMqlParamObj *cell=NULL; MqlParam param={}; //--- Выбираем всю историю if(!HistorySelect(0,TimeCurrent())) return; //--- Создаём список сделок в массиве массивов (CList in CList) //--- (одна строка - одна сделка, столбцы - объекты свойств сделки) int total=HistoryDealsTotal(); for(int i=0;i<total;i++) { ulong ticket=HistoryDealGetTicket(i); if(ticket==0) continue; //--- Добавляем к списку сделок новую строку свойств очередной сделки CList *row=DataListCreator::AddNewRowToDataList(&rows_data); if(row==NULL) continue; //--- Создаём "ячейки" с параметрами сделки и //--- добавляем их к созданной строке свойств сделки string symbol=HistoryDealGetString(ticket,DEAL_SYMBOL); int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); //--- Время совершения сделки (столбец 0) param.type=TYPE_DATETIME; param.integer_value=HistoryDealGetInteger(ticket,DEAL_TIME); param.double_value=(TIME_DATE|TIME_MINUTES|TIME_SECONDS); DataListCreator::AddNewCellParamToRow(row,param); //--- Имя символа (столбец 1) param.type=TYPE_STRING; param.string_value=symbol; DataListCreator::AddNewCellParamToRow(row,param); //--- Тикет сделки (столбец 2) param.type=TYPE_LONG; param.integer_value=(long)ticket; DataListCreator::AddNewCellParamToRow(row,param); //--- Ордер, на основание которого выполнена сделка (столбец 3) param.type=TYPE_LONG; param.integer_value=HistoryDealGetInteger(ticket,DEAL_ORDER); DataListCreator::AddNewCellParamToRow(row,param); //--- Идентификатор позиции (столбец 4) param.type=TYPE_LONG; param.integer_value=HistoryDealGetInteger(ticket,DEAL_POSITION_ID); DataListCreator::AddNewCellParamToRow(row,param); //--- Тип сделки (столбец 5) param.type=TYPE_STRING; ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket,DEAL_TYPE); param.integer_value=deal_type; string type=""; switch(deal_type) { case DEAL_TYPE_BUY : type="Buy"; break; case DEAL_TYPE_SELL : type="Sell"; break; case DEAL_TYPE_BALANCE : type="Balance"; break; case DEAL_TYPE_CREDIT : type="Credit"; break; case DEAL_TYPE_CHARGE : type="Charge"; break; case DEAL_TYPE_CORRECTION : type="Correction"; break; case DEAL_TYPE_BONUS : type="Bonus"; break; case DEAL_TYPE_COMMISSION : type="Commission"; break; case DEAL_TYPE_COMMISSION_DAILY : type="Commission daily"; break; case DEAL_TYPE_COMMISSION_MONTHLY : type="Commission monthly"; break; case DEAL_TYPE_COMMISSION_AGENT_DAILY : type="Commission agent daily"; break; case DEAL_TYPE_COMMISSION_AGENT_MONTHLY: type="Commission agent monthly"; break; case DEAL_TYPE_INTEREST : type="Interest"; break; case DEAL_TYPE_BUY_CANCELED : type="Buy canceled"; break; case DEAL_TYPE_SELL_CANCELED : type="Sell canceled"; break; case DEAL_DIVIDEND : type="Dividend"; break; case DEAL_DIVIDEND_FRANKED : type="Dividend franked"; break; case DEAL_TAX : type="Tax"; break; default : break; } param.string_value=type; DataListCreator::AddNewCellParamToRow(row,param); //--- Направление сделки (столбец 6) param.type=TYPE_STRING; ENUM_DEAL_ENTRY deal_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY); param.integer_value=deal_entry; string entry=""; switch(deal_entry) { case DEAL_ENTRY_IN : entry="In"; break; case DEAL_ENTRY_OUT : entry="Out"; break; case DEAL_ENTRY_INOUT : entry="InOut"; break; case DEAL_ENTRY_OUT_BY : entry="OutBy"; break; default : break; } param.string_value=entry; DataListCreator::AddNewCellParamToRow(row,param); //--- Объем сделки (столбец 7) param.type=TYPE_DOUBLE; param.double_value=HistoryDealGetDouble(ticket,DEAL_VOLUME); param.integer_value=2; DataListCreator::AddNewCellParamToRow(row,param); //--- Цена сделки (столбец 8) param.type=TYPE_DOUBLE; param.double_value=HistoryDealGetDouble(ticket,DEAL_PRICE); param.integer_value=(param.double_value>0 ? digits : 1); DataListCreator::AddNewCellParamToRow(row,param); //--- Уровень Stop Loss (столбец 9) param.type=TYPE_DOUBLE; param.double_value=HistoryDealGetDouble(ticket,DEAL_SL); param.integer_value=(param.double_value>0 ? digits : 1); DataListCreator::AddNewCellParamToRow(row,param); //--- Уровень Take Profit (столбец 10) param.type=TYPE_DOUBLE; param.double_value=HistoryDealGetDouble(ticket,DEAL_TP); param.integer_value=(param.double_value>0 ? digits : 1); DataListCreator::AddNewCellParamToRow(row,param); //--- Финансовый результат сделки (столбец 11) param.type=TYPE_DOUBLE; param.double_value=HistoryDealGetDouble(ticket,DEAL_PROFIT); param.integer_value=(param.double_value!=0 ? 2 : 1); DataListCreator::AddNewCellParamToRow(row,param); //--- Magic number для сделки (столбец 12) param.type=TYPE_LONG; param.integer_value=HistoryDealGetInteger(ticket,DEAL_MAGIC); DataListCreator::AddNewCellParamToRow(row,param); //--- Причина или источник проведения сделки (столбец 13) param.type=TYPE_STRING; ENUM_DEAL_REASON deal_reason=(ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON); param.integer_value=deal_reason; string reason=""; switch(deal_reason) { case DEAL_REASON_CLIENT : reason="Client"; break; case DEAL_REASON_MOBILE : reason="Mobile"; break; case DEAL_REASON_WEB : reason="Web"; break; case DEAL_REASON_EXPERT : reason="Expert"; break; case DEAL_REASON_SL : reason="SL"; break; case DEAL_REASON_TP : reason="TP"; break; case DEAL_REASON_SO : reason="StopOut"; break; case DEAL_REASON_ROLLOVER : reason="Rollover"; break; case DEAL_REASON_VMARGIN : reason="VMargin"; break; case DEAL_REASON_SPLIT : reason="Split"; break; case DEAL_REASON_CORPORATE_ACTION: reason="Corporate action"; break; default : break; } param.string_value=reason; DataListCreator::AddNewCellParamToRow(row,param); //--- Комментарий к сделке (столбец 14) param.type=TYPE_STRING; param.string_value=HistoryDealGetString(ticket,DEAL_COMMENT); DataListCreator::AddNewCellParamToRow(row,param); } //--- Объявляем и инициализируем заголовок таблицы string headers[]={"Time","Symbol","Ticket","Order","Position","Type","Entry","Volume","Price","SL","TP","Profit","Magic","Reason","Comment"}; //--- Создаём таблицу на основе созданного списка параметров и массива заголовков CTableByParam *table=new CTableByParam(rows_data,headers); if(table==NULL) return; //--- Распечатываем таблицу в журнале и удаляем созданный объект table.Print(); delete table; }
Di conseguenza, verrà stampata una tabella di tutte le operazioni con una larghezza di cella di 19 caratteri (per impostazione predefinita nel metodo Print della classe della tabella):
Table By Param: Rows total: 797, Columns total: 15: | n/n | Time | Symbol | Ticket | Order | Position | Type | Entry | Volume | Price | SL | TP | Profit | Magic | Reason | Comment | | 0 |2025.01.01 10:20:10 | | 3152565660 | 0 | 0 | Balance | In | 0.00 | 0.0 | 0.0 | 0.0 | 100000.00 | 0 | Client | | | 1 |2025.01.02 00:01:31 | GBPAUD | 3152603334 | 3191672408 | 3191672408 | Sell | In | 0.25 | 2.02111 | 0.0 | 0.0 | 0.0 | 112 | Expert | | | 2 |2025.01.02 02:50:31 | GBPAUD | 3152749152 | 3191820118 | 3191672408 | Buy | Out | 0.25 | 2.02001 | 0.0 | 2.02001 | 17.04 | 112 | TP | [tp 2.02001] | | 3 |2025.01.02 04:43:43 | GBPUSD | 3152949278 | 3191671491 | 3191671491 | Sell | In | 0.10 | 1.25270 | 0.0 | 1.24970 | 0.0 | 12 | Expert | | ... ... | 793 |2025.04.18 03:22:11 | EURCAD | 3602552747 | 3652159095 | 3652048415 | Sell | Out | 0.25 | 1.57503 | 0.0 | 1.57503 | 12.64 | 112 | TP | [tp 1.57503] | | 794 |2025.04.18 04:06:52 | GBPAUD | 3602588574 | 3652200103 | 3645122489 | Sell | Out | 0.25 | 2.07977 | 0.0 | 2.07977 | 3.35 | 112 | TP | [tp 2.07977] | | 795 |2025.04.18 04:06:52 | GBPAUD | 3602588575 | 3652200104 | 3652048983 | Sell | Out | 0.25 | 2.07977 | 0.0 | 2.07977 | 12.93 | 112 | TP | [tp 2.07977] | | 796 |2025.04.18 05:57:48 | AUDJPY | 3602664574 | 3652277665 | 3652048316 | Buy | Out | 0.25 | 90.672 | 0.0 | 90.672 | 19.15 | 112 | TP | [tp 90.672] |
L'esempio qui riportato mostra le prime e le ultime quattro operazioni, ma fornisce un'idea della tabella stampata nel registro.
Tutti i file creati vengono allegati all'articolo per consentirne lo studio autonomo. Il file di archivio può essere decompresso nella cartella del terminale e tutti i file saranno posizionati nella cartella desiderata: MQL5\Script\Tables.
Conclusioni
Abbiamo quindi completato il lavoro sui componenti di base del modello di tabella (Model) nell'ambito dell'architettura MVC. Abbiamo creato classi per lavorare con tabelle e intestazioni e le abbiamo testate su diversi tipi di dati: array bidimensionali, matrici e cronologia dei trade.
Passiamo ora alla fase successiva: lo sviluppo dei componenti View e Controller. In MQL5, questi due componenti sono strettamente collegati grazie al sistema di eventi integrato che consente agli oggetti di reagire alle azioni dell'utente.
Ciò ci offre l'opportunità di sviluppare contemporaneamente la visualizzazione della tabella (componente View) e il suo controllo (componente Controller). Ciò semplificherà leggermente l'implementazione piuttosto complessa e multilivello del componente View.
Tutti gli esempi e i file dell'articolo sono disponibili per il download. Nei prossimi articoli creeremo il componente View combinato con Controller per implementare uno strumento completo per le operazioni con le tabelle in MQL5.
Una volta completato il progetto, si apriranno nuove opportunità per creare altri controlli dell'interfaccia utente (UI) da utilizzare nei nostri sviluppi.
Programmi utilizzati nell'articolo:
| # | Nome | Tipo | Descrizione |
|---|---|---|---|
| 1 | Tables.mqh | Libreria di Classi | Libreria di classi per la creazione di tabelle |
| 2 | TestEmptyTable.mq5 | Script | Uno script per testare la creazione di una tabella vuota con un numero impostato di righe e colonne |
| 3 | TestTArrayTable.mq5 | Script | Uno script per testare la creazione di una tabella basata su un array di dati bidimensionale |
| 4 | TestMatrixTable.mq5 | Script | Uno script per testare la creazione di una tabella basata su una matrice di dati |
| 5 | TestDealsTable.mq5 | Script | Uno script per testare la creazione di una tabella basata sui dati dell’utente (cronologia dei trade) |
| 6 | MQL5.zip | Archivio | Un archivio dei file presentati sopra per la decompressione nella directory MQL5 del terminale client |
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/17803
Avvertimento: Tutti i diritti su questi materiali sono riservati a MetaQuotes Ltd. La copia o la ristampa di questi materiali in tutto o in parte sono proibite.
Questo articolo è stato scritto da un utente del sito e riflette le sue opinioni personali. MetaQuotes Ltd non è responsabile dell'accuratezza delle informazioni presentate, né di eventuali conseguenze derivanti dall'utilizzo delle soluzioni, strategie o raccomandazioni descritte.
I metodi di William Gann (Parte I): Creazione dell'indicatore Angoli di Gann
Implementazione di un modello di tabella in MQL5: Applicazione del concetto MVC
Utilizza i canali MQL5.community e le chat di gruppo
Passaggio a MQL5 Algo Forge (Parte 4): Lavorare con le Versioni e i Rilasci
- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso