MQL5'te bir tablo modelinin uygulanması: MVC konseptini uygulama
İçindekiler
- Giriş
- MVC konsepti (Model-View-Controller) hakkında biraz bilgi
- Bir tablo modeli oluşturmak için sınıflar yazma
- Tablo verilerini depolamanın temeli olarak bağlantılı listeler
- Tablo hücresi sınıfı
- Tablo satırı sınıfı
- Tablo modeli sınıfı
- Sonucun test edilmesi
- Sonuç
Giriş
Programlamada, uygulama mimarisi güvenilirlik, ölçeklenebilirlik ve destek kolaylığı sağlamada önemli bir rol oynar. Bu tür hedeflere ulaşmaya yardımcı olan yaklaşımlardan biri, MVC (Model-View-Controller) mimari modelden yararlanmaktır.
MVC konsepti, bir uygulamayı birbiriyle ilişkili üç bileşene ayırmanıza olanak tanır: model (veri ve mantık yönetimi), görünüm (veri görüntüleme) ve kontrolcü (kullanıcı eylemlerini işleme). Bu ayrım, kod geliştirme, test ve bakımı basitleştirerek daha yapılandırılmış ve esnek hale getirir.
Bu makalede, MQL5 dilinde bir tablo modeli uygulamak için MVC ilkelerinin nasıl uygulanacağını ele alacağız. Tablolar verilerin depolanması, işlenmesi ve görüntülenmesi için önemli bir araçtır ve uygun şekilde düzenlenmeleri bilgilerle çalışmayı çok daha kolay hale getirebilir. Tablolarla çalışmak için sınıflar oluşturacağız: tablo hücresi, tablo satırı ve tablo modeli. Hücreleri satırlar içinde ve satırları tablo modeli içinde depolamak için, verilerin verimli bir şekilde depolanmasına ve kullanılmasına olanak tanıyan MQL5 Standart Kütüphanesindeki bağlantılı liste sınıflarını kullanacağız.
MVC konsepti hakkında biraz bilgi: Nedir ve neden istiyoruz?
Uygulamayı bir tiyatro prodüksiyonu olarak düşünün. Nelerin olması gerektiğini tanımlayan bir senaryo vardır (bu modeldir). Sahne vardır - izleyicinin gördüğü şey (bu görünümdür). Ve son olarak, tüm süreci yöneten ve diğer unsurları birbirine bağlayan bir yönetmen vardır (bu kontrolcüdür). MVC (Model-View-Controller) mimari modelinin çalışma şekli budur.
Bu konsept, uygulama içindeki sorumlulukların ayrılmasına yardımcı olur. Model veri ve mantıktan sorumludur, görünüm görüntüleme ve görsellikten sorumludur ve kontrolcü kullanıcı eylemlerini işlemekten sorumludur. Bu tür bir ayrım, kodu daha net, daha esnek ve ekip çalışması için daha uygun hale getirir.
Diyelim ki bir tablo oluşturuyorsunuz. Model, hangi satırları ve hücreleri içerdiğini ve bunların nasıl değiştirileceğini bilir. Görünüm ekrana bir tablo çizer. Kullanıcı "Satır ekle" seçeneğine tıkladığında kontrolcü tepki verir ve görevi modele iletir ve ardından görünüme güncelleme yapmasını söyler.
MVC özellikle uygulama daha karmaşık hale geldiğinde kullanışlıdır: yeni özellikler eklendiğinde, arayüz değiştiğinde ve birkaç geliştirici çalıştığında. Net bir mimari ile değişiklikler yapmak, bileşenleri ayrı ayrı test etmek ve kodu yeniden kullanmak daha kolaydır.
Bu yaklaşımın bazı dezavantajları da vardır. Çok basit projeler için MVC gereksiz olabilir - birkaç fonksiyona sığabilecek şeyleri bile ayırmak gerekecektir. Ancak ölçeklenebilir, ciddi uygulamalar için bu yapı hızlı bir şekilde karşılığını verir.
Özetlersek:
MVC, kodu organize etmeye, daha anlaşılır, test edilebilir ve ölçeklenebilir hale getirmeye yardımcı olan güçlü bir mimari şablondur. Özellikle veri mantığı, kullanıcı arayüzü ve yönetimin birbirinden ayrılmasının önemli olduğu karmaşık uygulamalar için kullanışlıdır. Küçük projeler için kullanımı gereksizdir.
Model-View-Controller paradigması bizim görevimize çok iyi uyuyor. Tablo bağımsız nesnelerden oluşturulacaktır:
- Tablo hücresi.
Reel, tamsayı veya dizge türlerinden birinin değerini depolayan bir nesnedir. Değeri yönetmek, ayarlamak ve almak için araçlara sahiptir; - Tablo satırı.
Tablo hücresi nesnelerinin bir listesini depolayan bir nesnedir. Hücreleri, bunların konumlarını, ekleme ve silme işlemlerini yönetmek için araçlara sahiptir; - Tablo modeli.
Tablo satırı nesnelerinin bir listesini depolayan bir nesnedir. Tablo satırlarını ve sütunlarını, bunların konumlarını, ekleme ve silme işlemlerini yönetmek için araçlara sahiptir ve ayrıca satır ve hücre kontrollerine erişimi vardır.
Aşağıdaki şekil şematik olarak bir 4x4 tablo modelinin yapısını göstermektedir:

Şekil 1. 4x4 tablo modeli
Şimdi teoriden pratiğe geçelim.
Bir tablo modeli oluşturmak için sınıflar yazma
Tüm nesneleri oluşturmak için MQL5 Standart Kütüphanesini kullanacağız.
Her nesne kütüphanenin temel sınıfından kalıtılacaktır. Böylece bu nesneleri nesne listelerinde depolayabileceğiz.
Tüm sınıfları tek bir test komut dosyasına yazacağız, böylece her şey tek bir dosyada, görünür ve hızlı bir şekilde erişilebilir olacaktır. Gelecekte, yazdığımız sınıfları ayrı include dosyalarına dağıtacağız.
1. Tablo verilerini depolamanın temeli olarak bağlantılı listeler
CList bağlantılı liste, tablo verilerini depolamak için çok uygundur. Benzer CArrayObj listesinden farklı olarak, mevcut nesnenin solunda ve sağında bulunan komşu liste nesnelerine erişim metotlarına sahiptir. Bu, bir satırdaki hücreleri taşımayı veya bir tablodaki satırları taşımayı, eklemeyi ve silmeyi kolaylaştıracaktır. Aynı zamanda, listedeki taşınan, eklenen veya silinen nesnelerin doğru şekilde indekslenmesini de listenin kendisi sağlayacaktır.
Ancak burada bir nüans var. Bir listeyi bir dosyadan yükleme ve bir dosyaya kaydetme metotlarına bakarsanız, bir dosyadan yükleme yaparken liste sınıfının sanal CreateElement() metodunda yeni bir nesne oluşturması gerektiğini görebilirsiniz.
Bu sınıftaki bu metot basitçe NULL geri döndürür:
//--- method of creating an element of the list virtual CObject *CreateElement(void) { return(NULL); }
Bu, bağlantılı listelerle çalışmak için ve dosya işlemlerine ihtiyacımız olması durumunda, CList sınıfından kalıtım almamız ve bu metodu sınıfımızda uygulamamız gerektiği anlamına gelir.
Standart Kütüphane nesnelerini bir dosyaya kaydetme metotlarına bakarsanız, nesne özelliklerini kaydetme algoritmasının şu şekilde olduğunu görebilirsiniz:
- Veri başlangıcı işareti (-1) dosyaya yazılır,
- Nesne türü dosyaya yazılır,
- Tüm nesne özellikleri dosyaya teker teker yazılır.
Birinci ve ikinci maddeler, Standart Kütüphane nesnelerinin sahip olduğu tüm kaydetme/yükleme metotlarında mevcuttur. Buna göre, aynı mantığı izleyerek, listede depolanan nesnenin türünü bilmek istiyoruz, böylece bir dosyadan okurken, CList'ten kalıtılan liste sınıfının sanal CreateElement() metodunda bu türde bir nesne oluşturabiliriz.
Ayrıca, listeye yüklenebilecek tüm nesneler (sınıfları) liste sınıfı uygulanmadan önce bildirilmeli veya oluşturulmalıdır. Bu durumda, liste hangi nesnelerin "söz konusu" olduğunu ve hangilerinin oluşturulması gerektiğini "bilecektir".
\MQL5\Scripts\ terminal dizininde yeni bir TableModel\ klasörü ve içinde yeni bir TableModelTest.mq5 test komut dosyası oluşturalım.
Bağlantılı liste dosyasını bağlayalım ve gelecekteki tablo modeli sınıflarını bildirelim:
//+------------------------------------------------------------------+ //| TableModelTest.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 <Arrays\List.mqh> //--- Форвард-декларация классов class CTableCell; // Класс ячейки таблицы class CTableRow; // Класс строки таблицы class CTableModel; // Класс модели таблицы
CList'ten kalıtılan bağlantılı liste sınıfının bu sınıflar hakkında bilgi sahibi olması ve oluşturması gereken nesne türlerini bilmesi için gelecekte kullanılacak sınıfların burada ileriye dönük olarak bildirilmesi gereklidir. Bunu yapmak için, nesne türlerinin numaralandırmasını, yardımcı makroları ve listeleri sıralama yöntemlerinin numaralandırmasını yazacağız:
//+------------------------------------------------------------------+ //| Включаемые библиотеки | //+------------------------------------------------------------------+ #include <Arrays\List.mqh> //--- Форвард-декларация классов class CTableCell; // Класс ячейки таблицы class CTableRow; // Класс строки таблицы class CTableModel; // Класс модели таблицы //+------------------------------------------------------------------+ //| Макросы | //+------------------------------------------------------------------+ #define MARKER_START_DATA -1 // Маркер начала данных в файле #define MAX_STRING_LENGTH 128 // Максимальная длина строки в ячейке //+------------------------------------------------------------------+ //| Перечисления | //+------------------------------------------------------------------+ enum ENUM_OBJECT_TYPE // Перечисление типов объектов { OBJECT_TYPE_TABLE_CELL=10000, // Ячейка таблицы OBJECT_TYPE_TABLE_ROW, // Строка таблицы OBJECT_TYPE_TABLE_MODEL, // Модель таблицы }; enum ENUM_CELL_COMPARE_MODE // Режимы сравнения ячеек таблицы { CELL_COMPARE_MODE_COL, // Сравнение по номеру колонки CELL_COMPARE_MODE_ROW, // Сравнение по номеру строки CELL_COMPARE_MODE_ROW_COL, // Сравнение по строке и колонке }; //+------------------------------------------------------------------+ //| Функции | //+------------------------------------------------------------------+ //--- Возвращает тип объекта как строку string TypeDescription(const ENUM_OBJECT_TYPE type) { string array[]; int total=StringSplit(EnumToString(type),StringGetCharacter("_",0),array); string result=""; for(int i=2;i<total;i++) { array[i]+=" "; array[i].Lower(); array[i].SetChar(0,ushort(array[i].GetChar(0)-0x20)); result+=array[i]; } result.TrimLeft(); result.TrimRight(); return result; } //+------------------------------------------------------------------+ //| Классы | //+------------------------------------------------------------------+
Nesne türünün açıklamasını geri döndüren fonksiyon, nesne türü sabitlerinin tüm adlarının "OBJECT_TYPE_" alt dizgesiyle başladığı varsayımına dayanır. Ardından, bunu izleyen alt dizgeyi alabilir, ortaya çıkan dizgedeki tüm karakterleri küçük harfe dönüştürebilir, ilk karakteri büyük harfe dönüştürebilir ve soldaki ve sağdaki son dizgeden tüm boşlukları ve kontrol karakterlerini temizleyebiliriz.
Kendi bağlantılı liste sınıfımızı yazalım. Kodu aynı dosyada yazmaya devam edeceğiz:
//+------------------------------------------------------------------+ //| Классы | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс связанного списка объектов | //+------------------------------------------------------------------+ class CListObj : public CList { protected: ENUM_OBJECT_TYPE m_element_type; // Тип создаваемого объекта в CreateElement() public: //--- Виртуальный метод (1) загрузки списка из файла, (2) создания элемента списка virtual bool Load(const int file_handle); virtual CObject *CreateElement(void); };
CListObj sınıfı, Standart Kütüphane CList sınıfından kalıtılan yeni bağlantılı liste sınıfımızdır.
Sınıftaki tek değişken, oluşturulmakta olan nesnenin türünün yazılacağı değişken olacaktır. CreateElement() metodu sanal olduğundan ve üst sınıf metoduyla tam olarak aynı imzaya sahip olması gerektiğinden, oluşturulan nesnenin türünü bu metoda iletemeyiz. Ancak bu türü bildirilen bir değişkene yazabilir ve oluşturulan nesnenin türünü buradan okuyabiliriz.
Üst sınıfın iki sanal metodunu yeniden tanımlamalıyız: bir dosyadan yükleme metodu ve yeni bir nesne oluşturma metodu. Şimdi bunları inceleyelim.
Bir dosyadan liste yükleme metodu:
//+------------------------------------------------------------------+ //| Загрузка списка из файла | //+------------------------------------------------------------------+ bool CListObj::Load(const int file_handle) { //--- Переменные CObject *node; bool result=true; //--- Проверяем хэндл if(file_handle==INVALID_HANDLE) return(false); //--- Загрузка и проверка маркера начала списка - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return(false); //--- Загрузка и проверка типа списка if(::FileReadInteger(file_handle,INT_VALUE)!=Type()) return(false); //--- Чтение размера списка (количество объектов) uint num=::FileReadInteger(file_handle,INT_VALUE); //--- Последовательно заново создаём элементы списка с помощью вызова метода Load() объектов node this.Clear(); for(uint i=0; i<num; i++) { //--- Читаем и проверяем маркер начала данных объекта - 0xFFFFFFFFFFFFFFFF if(::FileReadLong(file_handle)!=MARKER_START_DATA) return false; //--- Читаем тип объекта this.m_element_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE); node=this.CreateElement(); if(node==NULL) return false; this.Add(node); //--- Сейчас файловый указатель смещён относительно начала маркера объекта на 12 байт (8 - маркер, 4 - тип) //--- Поставим указатель на начало данных объекта и загрузим свойства объекта из файла методом Load() элемента node. if(!::FileSeek(file_handle,-12,SEEK_CUR)) return false; result &=node.Load(file_handle); } //--- Результат return result; }
Burada, önce listenin başlangıcı, türü ve boyutu, yani listedeki eleman sayısı kontrol edilir; daha sonra, eleman sayısına göre bir döngü içinde, her nesnenin veri başlangıcı işareti ve türü dosyadan okunur. Elde edilen tür m_element_type değişkenine yazılır ve yeni bir eleman oluşturmak için bir metot çağrılır. Bu metotta, elde edilen türde yeni bir eleman oluşturulur ve node işaretçi (pointer) değişkenine yazılır, bu da listeye eklenir. Metodun tüm mantığı yorumlarda ayrıntılı olarak açıklanmıştır. Yeni bir liste öğesi oluşturma metodunu ele alalım.
Liste öğesi oluşturma metodu:
//+------------------------------------------------------------------+ //| Метод создания элемента списка | //+------------------------------------------------------------------+ 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(); default : return NULL; } }
Bu, metot çağrılmadan önce, oluşturulmakta olan nesnenin türünün m_element_type değişkenine zaten yazılmış olduğu anlamına gelir. Öğe türüne bağlı olarak, uygun türde yeni bir nesne oluşturulur ve buna bir işaretçi (pointer) geri döndürülür. Gelecekte, yeni kontroller geliştirilirken, türleri ENUM_OBJECT_TYPE numaralandırmasına yazılacaktır. Ve yeni nesne türleri oluşturmak için buraya yeni durumlar eklenecektir. Standart CList'i temel alan bağlantılı liste sınıfı hazırdır. Artık bilinen türlerdeki tüm nesneleri depolayabilir, listeleri bir dosyaya kaydedebilir ve bir dosyadan yükleyebilir ve doğru şekilde geri getirebilir.
2. Tablo hücresi sınıfı
Bir tablo hücresi, bir tablonun belirli bir değeri depolayan en basit öğesidir. Hücreler, tablo satırlarını temsil eden listeleri oluşturur. Her liste bir tablo satırını temsil eder. Tablomuzda, hücreler bir seferde birkaç türden yalnızca bir değer depolayabilecektir - reel, tamsayı veya dizge değeri.
Basit bir değere ek olarak, bir hücreye ENUM_OBJECT_TYPE numaralandırmasından bilinen türde bir nesne atanabilir. Bu durumda, hücre listelenen türlerden herhangi birinin değerini ve ayrıca türü özel bir değişkene yazılan bir nesnenin işaretçisini depolayabilir. Böylece, gelecekte Görünüm bileşenine, Kontrolcü bileşenini kullanarak etkileşimde bulunmak için bir hücrede böyle bir nesneyi görüntülemesi talimatı verilebilir.
Bir hücrede birkaç farklı türde değer depolanabileceğinden, bunları yazmak, depolamak ve geri döndürmek için birlik (union) kullanacağız. Birlik, birkaç alanı (field) aynı bellek bölümünde depolayan özel bir veri türüdür. Birlik bir yapıya benzer, ancak burada, bir yapıdan farklı olarak, birliğin farklı üyeleri aynı bellek bölümüne aittir. Yapı içindeyken, her alana (field) kendi bellek bölümü tahsis edilir.
Kodu önceden oluşturulmuş dosyaya yazmaya devam edelim. Yeni bir sınıf yazmaya başlayalım. Protected bölümde, bir birlik yazıyoruz ve değişkenleri bildiriyoruz:
//+------------------------------------------------------------------+ //| Класс ячейки таблицы | //+------------------------------------------------------------------+ 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; // Флаг редактируемой ячейки public:
Public bölümde, bir hücrede depolanan çeşitli veri türleri için protected değişkenlere, sanal metotlara ve sınıf yapıcılarına erişim metotları yazıyoruz:
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(::DoubleToString(this.ValueD(),this.Digits())); case TYPE_LONG : return(::IntegerToString(this.ValueL())); case TYPE_DATETIME: return(::TimeToString(this.ValueL(),this.m_time_flags)); case TYPE_COLOR : return(::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 SetRow(const uint row) { this.m_row=(int)row; } void SetCol(const uint col) { this.m_col=(int)col; } void SetDatatype(const ENUM_DATATYPE datatype) { this.m_datatype=datatype; } void SetDigits(const int digits) { this.m_digits=digits; } void SetDatetimeFlags(const uint flags) { this.m_time_flags=flags; } void SetColorNameFlag(const bool flag) { this.m_color_flag=flag; } void SetEditable(const bool flag) { this.m_editable=flag; } void SetPositionInTable(const uint row,const uint col) { this.SetRow(row); this.SetCol(col); } //--- Назначает объект в ячейку void AssignObject(CObject *object) { if(object==NULL) { ::PrintFormat("%s: Error. Empty object passed",__FUNCTION__); return; } this.m_object=object; this.m_object_type=(ENUM_OBJECT_TYPE)object.Type(); } //--- Снимает назначение объекта void UnassignObject(void) { this.m_object=NULL; this.m_object_type=-1; } //--- Устанавливает double-значение void SetValue(const double value) { this.m_datatype=TYPE_DOUBLE; if(this.m_editable) this.m_datatype_value.SetValueD(value); } //--- Устанавливает long-значение void SetValue(const long value) { this.m_datatype=TYPE_LONG; if(this.m_editable) this.m_datatype_value.SetValueL(value); } //--- Устанавливает datetime-значение void SetValue(const datetime value) { this.m_datatype=TYPE_DATETIME; if(this.m_editable) this.m_datatype_value.SetValueL(value); } //--- Устанавливает color-значение void SetValue(const color value) { this.m_datatype=TYPE_COLOR; if(this.m_editable) this.m_datatype_value.SetValueL(value); } //--- Устанавливает string-значение void SetValue(const string value) { this.m_datatype=TYPE_STRING; if(this.m_editable) this.m_datatype_value.SetValueS(value); } //--- Очищает данные void ClearData(void) { if(this.Datatype()==TYPE_STRING) this.SetValue(""); else this.SetValue(0.0); } //--- (1) Возвращает, (2) выводит в журнал описание объекта 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_TABLE_CELL);} //--- Конструкторы/деструктор CTableCell(void) : m_row(0), m_col(0), m_datatype(-1), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueD(0); } //--- Принимает double-значение CTableCell(const uint row,const uint col,const double value,const int digits) : m_row((int)row), m_col((int)col), m_datatype(TYPE_DOUBLE), m_digits(digits), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueD(value); } //--- Принимает long-значение CTableCell(const uint row,const uint col,const long value) : m_row((int)row), m_col((int)col), m_datatype(TYPE_LONG), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает datetime-значение CTableCell(const uint row,const uint col,const datetime value,const uint time_flags) : m_row((int)row), m_col((int)col), m_datatype(TYPE_DATETIME), m_digits(0), m_time_flags(time_flags), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает color-значение CTableCell(const uint row,const uint col,const color value,const bool color_names_flag) : m_row((int)row), m_col((int)col), m_datatype(TYPE_COLOR), m_digits(0), m_time_flags(0), m_color_flag(color_names_flag), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает string-значение CTableCell(const uint row,const uint col,const string value) : m_row((int)row), m_col((int)col), m_datatype(TYPE_STRING), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueS(value); } ~CTableCell(void) {} };
Değerleri ayarlama metotlarında, önce hücrede depolanan değerin türü ayarlanır ve ardından hücredeki değerleri düzenleme özelliğinin bayrağı kontrol edilir. Ve yalnızca bayrak ayarlandığında, yeni değer hücreye depolanır:
//--- Устанавливает double-значение void SetValue(const double value) { this.m_datatype=TYPE_DOUBLE; if(this.m_editable) this.m_datatype_value.SetValueD(value); }
Neden bu şekilde yapılıyor? Yeni bir hücre oluşturulurken, depolanan değerin türüyle oluşturulur. Değerin türünü değiştirmek istiyorsanız, ancak aynı zamanda hücre düzenlenebilir değilse, istenen türü ayarlamak için metodu çağırabilirsiniz. Depolanan değerin türü değiştirilecek, ancak hücredeki değer etkilenmeyecektir.
Veri temizleme metodu sayısal değerleri sıfıra, dizge değerlerini ise boş olarak ayarlar:
//--- Очищает данные void ClearData(void) { if(this.Datatype()==TYPE_STRING) this.SetValue(""); else this.SetValue(0.0); }
Daha sonra, temizlenen hücrelerde hiçbir veri görüntülenmemesi için bunu farklı şekilde yapacağız. Hücreyi boş hale getirmek için, hücre için bir “boş” değer oluşturacağız ve böylece hücre temizlendiğinde, içinde depolanan ve görüntülenen tüm değerler silinecektir. Sonuçta, sıfır da tam teşekküllü bir değerdir ve şu anda hücre temizlendiğinde sayısal veriler sıfırlarla doldurulur. Bu yanlıştır.
Sınıfın parametrik yapıcılarında, tablodaki hücre koordinatları iletilir - satır ve sütun numarası ve gerekli tür (double, long, datetime, color, string). Bazı türler ek bilgi gerektirir:
- double - çıktı değerinin doğruluğu (ondalık basamak sayısı),
- datetime - zaman çıktısı bayrakları (tarih/saat-dakika/saniye),
- color - bilinen standart renklerin adlarını görüntülemek için bayrak.
Hücrelerde depolanan bu tür değerlere sahip yapıcılarda, hücrelerde görüntülenen değerlerin biçimini ayarlamak için ek parametreler iletilir:
//--- Принимает double-значение CTableCell(const uint row,const uint col,const double value,const int digits) : m_row((int)row), m_col((int)col), m_datatype(TYPE_DOUBLE), m_digits(digits), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueD(value); } //--- Принимает long-значение CTableCell(const uint row,const uint col,const long value) : m_row((int)row), m_col((int)col), m_datatype(TYPE_LONG), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает datetime-значение CTableCell(const uint row,const uint col,const datetime value,const uint time_flags) : m_row((int)row), m_col((int)col), m_datatype(TYPE_DATETIME), m_digits(0), m_time_flags(time_flags), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает color-значение CTableCell(const uint row,const uint col,const color value,const bool color_names_flag) : m_row((int)row), m_col((int)col), m_datatype(TYPE_COLOR), m_digits(0), m_time_flags(0), m_color_flag(color_names_flag), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueL(value); } //--- Принимает string-значение CTableCell(const uint row,const uint col,const string value) : m_row((int)row), m_col((int)col), m_datatype(TYPE_STRING), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1) { this.m_datatype_value.SetValueS(value); }
İki nesneyi karşılaştırma metodu:
//+------------------------------------------------------------------+ //| Сравнение двух объектов | //+------------------------------------------------------------------+ int CTableCell::Compare(const CObject *node,const int mode=0) const { const CTableCell *obj=node; switch(mode) { case CELL_COMPARE_MODE_COL : return(this.Col()>obj.Col() ? 1 : this.Col()<obj.Col() ? -1 : 0); case CELL_COMPARE_MODE_ROW : return(this.Row()>obj.Row() ? 1 : this.Row()<obj.Row() ? -1 : 0); //---CELL_COMPARE_MODE_ROW_COL default : return ( this.Row()>obj.Row() ? 1 : this.Row()<obj.Row() ? -1 : this.Col()>obj.Col() ? 1 : this.Col()<obj.Col() ? -1 : 0 ); } }
Metot, iki nesnenin parametrelerini üç karşılaştırma kriterinden biriyle karşılaştırmaya olanak tanır - sütun numarasına göre, satır numarasına göre ve aynı anda satır ve sütun numaralarına göre.
Bu metot, tablo satırlarını tablo sütunlarının değerlerine göre sıralayabilmek için gereklidir. Bu, sonraki makalelerde Kontrolcü tarafından işlenecektir.
Hücre özelliklerini bir dosyaya kaydetme metodu:
//+------------------------------------------------------------------+ //| Сохранение в файл | //+------------------------------------------------------------------+ bool CTableCell::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_datatype,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем тип объекта в ячейке if(::FileWriteInteger(file_handle,this.m_object_type,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем номер строки if(::FileWriteInteger(file_handle,this.m_row,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем номер столбца if(::FileWriteInteger(file_handle,this.m_col,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем точность представления данных if(::FileWriteInteger(file_handle,this.m_digits,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем флаги отображения даты/времени if(::FileWriteInteger(file_handle,this.m_time_flags,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем флаг отображения наименования цвета if(::FileWriteInteger(file_handle,this.m_color_flag,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем флаг редактируемой ячейки if(::FileWriteInteger(file_handle,this.m_editable,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем значение if(::FileWriteStruct(file_handle,this.m_datatype_value)!=sizeof(this.m_datatype_value)) return(false); //--- Всё успешно return true; }
Veri başlangıcı işareti ve nesne türü dosyaya yazıldıktan sonra, tüm hücre özellikleri sırayla kaydedilir. Birlik, FileWriteStruct() kullanılarak bir yapı olarak kaydedilmelidir.
Bir dosyadan hücre özellikleri yükleme metodu:
//+------------------------------------------------------------------+ //| Загрузка из файла | //+------------------------------------------------------------------+ bool CTableCell::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_datatype=(ENUM_DATATYPE)::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем тип объекта в ячейке this.m_object_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем номер строки this.m_row=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем номер столбца this.m_col=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем точность представления данных this.m_digits=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем флаги отображения даты/времени this.m_time_flags=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем флаг отображения наименования цвета this.m_color_flag=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем флаг редактируемой ячейки this.m_editable=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем значение if(::FileReadStruct(file_handle,this.m_datatype_value)!=sizeof(this.m_datatype_value)) return(false); //--- Всё успешно return true; }
Veri başlangıcı işareti ve nesne türü okunduktan ve kontrol edildikten sonra, nesnenin tüm özellikleri kaydedildikleri sırayla yüklenir. Birlikler FileReadStruct() kullanılarak okunur.
Nesne açıklamasını geri döndüren metot:
//+------------------------------------------------------------------+ //| Возвращает описание объекта | //+------------------------------------------------------------------+ string CTableCell::Description(void) { return(::StringFormat("%s: Row %u, Col %u, %s <%s>Value: %s", TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.Row(),this.Col(), (this.m_editable ? "Editable" : "Uneditable"),this.DatatypeDescription(),this.Value())); }
Burada, bazı hücre parametrelerinden bir dizge oluşturulur ve örneğin double için şu formatta geri döndürülür:
Table Cell: Row 2, Col 2, Uneditable <double>Value: 0.00
Nesne açıklamasını günlüğe yazdıran metot:
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта | //+------------------------------------------------------------------+ void CTableCell::Print(void) { ::Print(this.Description()); }
Burada, nesne açıklaması basitçe günlüğe yazdırılır.
//+------------------------------------------------------------------+ //| Класс строки таблицы | //+------------------------------------------------------------------+ 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 *CreateNewCell(const double value); CTableCell *CreateNewCell(const long value); CTableCell *CreateNewCell(const datetime value); CTableCell *CreateNewCell(const color value); CTableCell *CreateNewCell(const string value); //--- Возвращает (1) ячейку по индексу, (2) количество ячеек CTableCell *GetCell(const uint index) { return this.m_list_cells.GetNodeAtIndex(index); } uint CellsTotal(void) const { return this.m_list_cells.Total(); } //--- Устанавливает значение в указанную ячейку void CellSetValue(const uint index,const double value); void CellSetValue(const uint index,const long value); void CellSetValue(const uint index,const datetime value); void CellSetValue(const uint index,const color value); void CellSetValue(const uint index,const string value); //--- (1) назначает в ячейку, (2) снимает с ячейки назначенный объект void CellAssignObject(const uint index,CObject *object); void CellUnassignObject(const uint index); //--- (1) Удаляет (2) перемещает ячейку bool CellDelete(const uint index); bool CellMoveTo(const uint cell_index, const uint index_to); //--- Обнуляет данные ячеек строки void ClearData(void); //--- (1) Возвращает, (2) выводит в журнал описание объекта string Description(void); void Print(const bool detail, const bool as_table=false, const int cell_width=10); //--- Виртуальные методы (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_ROW); } //--- Конструкторы/деструктор CTableRow(void) : m_index(0) {} CTableRow(const uint index) : m_index(index) {} ~CTableRow(void){} };
3. Tablo satırı sınıfı
Bir tablo satırı esasen hücrelerin bağlantılı bir listesidir. Satır sınıfı, yeni hücreler ekleyebilmeli, listedeki mevcut hücreleri silebilmeli ve yeni konumlara taşıyabilmelidir. Sınıf, listedeki hücreleri yönetmek için asgari düzeyde yeterli bir metot kümesine sahip olmalıdır.
Kodu aynı dosyaya yazmaya devam edelim. Sınıf parametrelerinde yalnızca bir değişken mevcuttur - tablodaki satır indeksi. Geri kalan her şey satır özellikleriyle ve hücrelerinin listesiyle çalışmaya yönelik metotlardır.
Sınıf metotlarını inceleyelim.
İki tablo satırını karşılaştırma metodu:
//+------------------------------------------------------------------+ //| Сравнение двух объектов | //+------------------------------------------------------------------+ int CTableRow::Compare(const CObject *node,const int mode=0) const { const CTableRow *obj=node; return(this.Index()>obj.Index() ? 1 : this.Index()<obj.Index() ? -1 : 0); }
Satırlar yalnızca tek bir parametreyle (satır indeksi) karşılaştırılabildiğinden, bu karşılaştırma burada gerçekleştirilir. Bu metot tablo satırlarını sıralamak için gerekli olacaktır.
Farklı türde depolanmış verilere sahip hücreler oluşturmak için aşırı yüklenmiş metotlar:
//+------------------------------------------------------------------+ //| Создаёт новую double-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const double value) { //--- Создаём новый объект ячейки, хранящей значение с типом double CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value,2); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; } //+------------------------------------------------------------------+ //| Создаёт новую long-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const long value) { //--- Создаём новый объект ячейки, хранящей значение с типом long CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; } //+------------------------------------------------------------------+ //| Создаёт новую datetime-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const datetime value) { //--- Создаём новый объект ячейки, хранящей значение с типом datetime CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value,TIME_DATE|TIME_MINUTES|TIME_SECONDS); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; } //+------------------------------------------------------------------+ //| Создаёт новую color-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const color value) { //--- Создаём новый объект ячейки, хранящей значение с типом color CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value,true); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; } //+------------------------------------------------------------------+ //| Создаёт новую string-ячейку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableCell *CTableRow::CreateNewCell(const string value) { //--- Создаём новый объект ячейки, хранящей значение с типом string CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value); if(cell==NULL) { ::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal()); return NULL; } //--- Добавляем созданную ячейку в конец списка if(!this.AddNewCell(cell)) { delete cell; return NULL; } //--- Возвращаем указатель на объект return cell; }
Yeni bir hücre (double, long, datetime, color, string) oluşturmak ve liste sonuna eklemek için beş metot. Hücreye veri çıktı formatının ek parametreleri varsayılan değerlerle ayarlanır. Hücre oluşturulduktan sonra değiştirilebilirler. Önce yeni bir hücre nesnesi oluşturulur ve ardından liste sonuna eklenir. Nesne oluşturulmamışsa, bellek sızıntılarını önlemek için silinir.
Liste sonuna bir hücre ekleyen metot:
//+------------------------------------------------------------------+ //| Добавляет ячейку в конец списка | //+------------------------------------------------------------------+ bool CTableRow::AddNewCell(CTableCell *cell) { //--- Если передан пустой объект - сообщаем и возвращаем false if(cell==NULL) { ::PrintFormat("%s: Error. Empty CTableCell object passed",__FUNCTION__); return false; } //--- Устанавливаем индекс ячейки в списке и добавляем созданную ячейку в конец списка cell.SetPositionInTable(this.m_index,this.CellsTotal()); if(this.m_list_cells.Add(cell)==WRONG_VALUE) { ::PrintFormat("%s: Error. Failed to add cell (%u,%u) to list",__FUNCTION__,this.m_index,this.CellsTotal()); return false; } //--- Успешно return true; }
Yeni oluşturulan herhangi bir hücre her zaman liste sonuna eklenir. Ardından, daha sonra oluşturulacak olan tablo modeli sınıfının metotları kullanılarak uygun konuma taşınabilir.
Belirtilen hücreye bir değer ayarlamak için aşırı yüklenmiş metotlar:
//+------------------------------------------------------------------+ //| Устанавливает double-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const double value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); } //+------------------------------------------------------------------+ //| Устанавливает long-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const long value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); } //+------------------------------------------------------------------+ //| Устанавливает datetime-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const datetime value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); } //+------------------------------------------------------------------+ //| Устанавливает color-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const color value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); } //+------------------------------------------------------------------+ //| Устанавливает string-значение в указанную ячейку | //+------------------------------------------------------------------+ void CTableRow::CellSetValue(const uint index,const string value) { //--- Получаем из списка нужную ячейку и записываем в неё новое значение CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.SetValue(value); }
İndeksi kullanarak, listeden gerekli hücreyi alır ve onun için bir değer ayarlarız. Hücre düzenlenebilir değilse, hücre nesnesinin SetValue() metodu hücre için yalnızca ayarlanan değerin türünü ayarlar. Değerin kendisi ayarlanmayacaktır.
Bir hücreye nesne atayan metot:
//+------------------------------------------------------------------+ //| Назначает в ячейку объект | //+------------------------------------------------------------------+ void CTableRow::CellAssignObject(const uint index,CObject *object) { //--- Получаем из списка нужную ячейку и записываем в неё указатель на объект CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.AssignObject(object); }
Hücre nesnesini indeksine göre alırız ve AssignObject() metodunu kullanarak nesnenin işaretçisini hücreye atarız.
Hücre için atanmış nesneyi iptal eden metot:
//+------------------------------------------------------------------+ //| Отменяет для ячейки назначенный объект | //+------------------------------------------------------------------+ void CTableRow::CellUnassignObject(const uint index) { //--- Получаем из списка нужную ячейку и отменяем в ней указатель на объект и его тип CTableCell *cell=this.GetCell(index); if(cell!=NULL) cell.UnassignObject(); }
Hücre nesnesini indeksine göre alırız ve hücreye atanmış nesnenin işaretçisini kaldırmak için UnassignObject() metodunu kullanırız.
Bir hücreyi silen metot:
//+------------------------------------------------------------------+ //| Удаляет ячейку | //+------------------------------------------------------------------+ bool CTableRow::CellDelete(const uint index) { //--- Удаляем ячейку в списке по индексу if(!this.m_list_cells.Delete(index)) return false; //--- Обновляем индексы для оставшихся ячеек в списке this.CellsPositionUpdate(); return true; }
CList sınıfının Delete() metodunu kullanarak listeden hücreyi sileriz. Listeden bir hücre silindikten sonra, kalan hücrelerin indeksleri değiştirilir. CellsPositionUpdate() metodunu kullanarak, listede kalan tüm hücrelerin indekslerini güncelleriz.
Bir hücreyi belirtilen konuma taşıyan metot:
//+------------------------------------------------------------------+ //| Перемещает ячейку на указанную позицию | //+------------------------------------------------------------------+ bool CTableRow::CellMoveTo(const uint cell_index,const uint index_to) { //--- Выбираем нужную ячейку по индексу в списке, делая её текущей CTableCell *cell=this.GetCell(cell_index); //--- Перемещаем текущую ячейку на указанную позицию в списке if(cell==NULL || !this.m_list_cells.MoveToIndex(index_to)) return false; //--- Обновляем индексы всех ячеек в списке this.CellsPositionUpdate(); return true; }
CList sınıfının bir nesne üzerinde işlem yapabilmesi için, listedeki bu nesnenin geçerli nesne olması gerekir. Örneğin, seçildiğinde geçerli hale gelir. Bu nedenle, burada önce listeden hücre nesnesini indeksine göre alırız. Hücre geçerli hücre haline gelir ve ardından CList sınıfının MoveToIndex() metodunu kullanarak nesneyi listede istenen konuma taşırız. Bir nesneyi başarıyla yeni bir konuma taşıdıktan sonra, kalan nesnelerin indeksleri ayarlanmalıdır; bu da CellsPositionUpdate() metodu kullanılarak yapılır.
Listedeki tüm hücreler için satır ve sütun konumlarını ayarlayan metot:
//+------------------------------------------------------------------+ //| Устанавливает позиции строки и колонки всем ячейкам | //+------------------------------------------------------------------+ void CTableRow::CellsPositionUpdate(void) { //--- В цикле по всем ячейкам в списке for(int i=0;i<this.m_list_cells.Total();i++) { //--- получаем очередную ячейку и устанавливаем в неё индексы строки и столбца CTableCell *cell=this.GetCell(i); if(cell!=NULL) cell.SetPositionInTable(this.Index(),this.m_list_cells.IndexOf(cell)); } }
CList sınıfı, listedeki geçerli nesne indeksini bulmamızı sağlar. Bunu yapmak için nesne seçilmelidir. Burada listedeki tüm hücre nesneleri arasında döngü yapar, her birini seçer ve CList sınıfının IndexOf() metodunu kullanarak indeksini buluruz. Satır indeksi ve bulunan hücre indeksi, SetPositionInTable() metodu kullanılarak hücre nesnesine ayarlanır.
Satır hücrelerinin verilerini sıfırlayan metot:
//+------------------------------------------------------------------+ //| Обнуляет данные ячеек строки | //+------------------------------------------------------------------+ void CTableRow::ClearData(void) { //--- В цикле по всем ячейкам в списке for(uint i=0;i<this.CellsTotal();i++) { //--- получаем очередную ячейку и устанавливаем в неё пустое значение CTableCell *cell=this.GetCell(i); if(cell!=NULL) cell.ClearData(); } }
Bir döngü içinde, ClearData() hücre nesnesi metodunu kullanarak listedeki her bir sonraki hücreyi sıfırlarız. Dizge verileri için hücreye boş bir dizge yazılır, sayısal veriler için ise sıfır yazılır.
Nesne açıklamasını geri döndüren metot:
//+------------------------------------------------------------------+ //| Возвращает описание объекта | //+------------------------------------------------------------------+ string CTableRow::Description(void) { return(::StringFormat("%s: Position %u, Cells total: %u", TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.Index(),this.CellsTotal())); }
Nesnenin özelliklerinden ve verilerinden bir dizge toplanır ve aşağıdaki biçimde geri döndürülür, örneğin:
Table Row: Position 1, Cells total: 4:
Nesne açıklamasını günlüğe yazdıran metot:
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта | //+------------------------------------------------------------------+ void CTableRow::Print(const bool detail, const bool as_table=false, const int cell_width=10) { //--- Количество ячеек int total=(int)this.CellsTotal(); //--- Если вывод в табличном виде string res=""; if(as_table) { //--- создаём строку таблицы из значений всех ячеек string head=" Row "+(string)this.Index(); string res=::StringFormat("|%-*s |",cell_width,head); for(int i=0;i<total;i++) { CTableCell *cell=this.GetCell(i); if(cell==NULL) continue; res+=::StringFormat("%*s |",cell_width,cell.Value()); } //--- Выводим строку в журнал ::Print(res); return; } //--- Выводим заголовок в виде описания строки ::Print(this.Description()+(detail ? ":" : "")); //--- Если детализированное описание if(detail) { //--- Вывод не в табличном виде //--- В цикле по спискук ячеек строки for(int i=0; i<total; i++) { //--- получаем текущую ячейку и добавляем в итоговую строку её описание CTableCell *cell=this.GetCell(i); if(cell!=NULL) res+=" "+cell.Description()+(i<total-1 ? "\n" : ""); } //--- Выводим в журнал созданную в цикле строку ::Print(res); } }
Günlüğe tablo biçiminde olmayan veri çıktısı için, önce başlık satır açıklaması olarak günlüğe yazdırılır. Ardından, ayrıntılı çıktı bayrağı ayarlanmışsa, her bir hücrenin açıklaması hücre listesi boyunca bir döngü içinde günlüğe yazdırılır.
Sonuç olarak, bir tablo satırının günlüğe ayrıntılı olarak çıktısı örneğin aşağıdaki gibi görünür (tablo biçiminde olmayan görünüm için):
Table Row: Position 0, Cells total: 4: Table Cell: Row 0, Col 0, Editable <long>Value: 10 Table Cell: Row 0, Col 1, Editable <long>Value: 21 Table Cell: Row 0, Col 2, Editable <long>Value: 32 Table Cell: Row 0, Col 3, Editable <long>Value: 43
Tablo biçiminde görünüm için sonuç örneğin aşağıdaki gibi olacaktır:
| Row 0 | 0 | 1 | 2 | 3 |
Bir tablo satırını bir dosyaya kaydetme metodu:
//+------------------------------------------------------------------+ //| Сохранение в файл | //+------------------------------------------------------------------+ bool CTableRow::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_index,INT_VALUE)!=INT_VALUE) return(false); //--- Сохраняем список ячеек if(!this.m_list_cells.Save(file_handle)) return(false); //--- Успешно return true; }
Veri başlangıcı işaretini, ardından nesne türünü kaydederiz. Bu, dosyadaki her nesnenin standart başlığıdır. Bundan sonra, nesne özellikleri dosyaya kaydedilir. Burada satır indeksi ve ardından hücre listesi dosyaya yazılır.
Bir dosyadan satır yükleme metodu:
//+------------------------------------------------------------------+ //| Загрузка из файла | //+------------------------------------------------------------------+ bool CTableRow::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_index=::FileReadInteger(file_handle,INT_VALUE); //--- Загружаем список ячеек if(!this.m_list_cells.Load(file_handle)) return(false); //--- Успешно return true; }
Burada her şey kaydederken olduğu gibi aynı sıradadır. İlk olarak, veri başlangıcı işareti ve nesne türü yüklenir ve kontrol edilir. Sonrasında satır indeksi ve tüm hücre listesi yüklenir.
4. Tablo modeli sınıfı
En basit haliyle, tablo modeli satırlardan oluşan bağlantılı bir listedir, ki bu satırlar da hücrelerden oluşan bağlantılı listelerdir. Bugün oluşturacağımız model, girdi olarak beş türden (double, long, datetime, color, string) birinin iki boyutlu bir dizisini alacak ve bundan sanal bir tablo oluşturacaktır. Gelecekte, bu sınıfı diğer girdi verilerinden tablolar oluşturmak için başka argümanlar kabul edecek şekilde genişleteceğiz. Aynı model varsayılan model olarak kabul edilecektir.
Kodu aynı \MQL5\Scripts\TableModel\TableModelTest.mq5 dosyasına yazmaya devam edelim.
Tablo modeli sınıfı esasen satırları, sütunları ve hücreleri yönetmek için metotları olan bağlantılı bir satır listesidir. Tüm değişkenler ve metotlarla birlikte sınıf gövdesini yazalım ve ardından sınıfın bildirilen metotlarını inceleyelim:
//+------------------------------------------------------------------+ //| Класс модели таблицы | //+------------------------------------------------------------------+ class CTableModel : public CObject { protected: CTableRow m_row_tmp; // Объект строки для поиска в списке CListObj m_list_rows; // Список строк таблицы //--- Создаёт модель таблицы из двумерного массива template<typename T> void CreateTableModel(T &array[][]); //--- Возвращает корректный тип данных 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 RowResetData(const uint index); //--- (1) Возвращает, (2) выводит в журнал описание строки string RowDescription(const uint index); void RowPrint(const uint index,const bool detail); //--- (1) Удаляет (2) перемещает столбец, (3) очищает данные столбца bool ColumnDelete(const uint index); bool ColumnMoveTo(const uint row_index, const uint index_to); void ColumnResetData(const uint index); //--- (1) Возвращает, (2) выводит в журнал описание таблицы string Description(void); void Print(const bool detail); void PrintTable(const int cell_width=10); //--- (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); } //--- Конструкторы/деструктор CTableModel(void){} CTableModel(double &array[][]) { this.CreateTableModel(array); } CTableModel(long &array[][]) { this.CreateTableModel(array); } CTableModel(datetime &array[][]) { this.CreateTableModel(array); } CTableModel(color &array[][]) { this.CreateTableModel(array); } CTableModel(string &array[][]) { this.CreateTableModel(array); } ~CTableModel(void){} };
Temel olarak, tablo satırlarının bağlantılı listesi için yalnızca bir nesne ve satırları, hücreleri ve sütunları yönetmek için metotlar vardır. Ve iki boyutlu dizilerin farklı türlerini kabul eden yapıcılar.
Sınıf yapıcısına bir dizi iletilir ve bir tablo modeli oluşturmak için bir metot çağrılır:
//+------------------------------------------------------------------+ //| Создаёт модель таблицы из двумерного массива | //+------------------------------------------------------------------+ template<typename T> void CTableModel::CreateTableModel(T &array[][]) { //--- Получаем из свойств массива количество строк и столбцов таблицы int rows_total=::ArrayRange(array,0); int cols_total=::ArrayRange(array,1); //--- В цикле по индексам строк for(int r=0; r<rows_total; r++) { //--- создаём новую пустую строку и добавляем её в конец списка строк CTableRow *row=this.CreateNewEmptyRow(); //--- Если строка создана и добавлена в список, if(row!=NULL) { //--- В цикле по количеству ячеек в строке //--- создаём все ячейки, добавляя каждую новую в конец списка ячеек строки for(int c=0; c<cols_total; c++) row.CreateNewCell(array[r][c]); } } }
Metodun mantığı yorumlarda açıklanmıştır. Dizinin ilk boyutu tablo satırları, ikinci boyutu ise her satırın hücreleridir. Hücreler oluşturulurken, metoda iletilen veri türü kullanılır.
Böylece, hücrelerin başlangıçta farklı veri türlerini (double, long, datetime, color, string) depoladığı birkaç tablo modeli oluşturabiliriz. Daha sonra, tablo modeli oluşturulduktan sonra, hücrelerde depolanan veri türleri değiştirilebilir.
Yeni bir boş satır oluşturan ve listenin sonuna ekleyen metot:
//+------------------------------------------------------------------+ //| Создаёт новую пустую строку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableRow *CTableModel::CreateNewEmptyRow(void) { //--- Создаём новый объект строки CTableRow *row=new CTableRow(this.m_list_rows.Total()); if(row==NULL) { ::PrintFormat("%s: Error. Failed to create new row at position %u",__FUNCTION__, this.m_list_rows.Total()); return NULL; } //--- Если строку не удалось добавить в список - удаляем созданный новый объект и возвращаем NULL if(!this.AddNewRow(row)) { delete row; return NULL; } //--- Успешно - возвращаем указатель на созданный объект return row; }
Metot, CTableRow sınıfının yeni bir nesnesini oluşturur ve AddNewRow() metodunu kullanarak satır listesinin sonuna ekler. Bir ekleme hatası oluşursa, oluşturulan yeni nesne silinir ve NULL geri döndürülür. Başarılı olduğunda, metot listeye yeni eklenen satırın işaretçisini geri döndürür.
Liste sonuna bir satır nesnesi ekleyen metot:
//+------------------------------------------------------------------+ //| Добавляет строку в конец списка | //+------------------------------------------------------------------+ bool CTableModel::AddNewRow(CTableRow *row) { //--- Если передан пустой объект - сообщаем об этом и возвращаем false if(row==NULL) { ::PrintFormat("%s: Error. Empty CTableRow object passed",__FUNCTION__); return false; } //--- Устанавливаем строке её индекс в списке и добавляем её в конец списка row.SetIndex(this.RowsTotal()); if(this.m_list_rows.Add(row)==WRONG_VALUE) { ::PrintFormat("%s: Error. Failed to add row (%u) to list",__FUNCTION__,row.Index()); return false; } //--- Успешно return true; }
Yukarıda bahsedilen metotların her ikisi de sınıfın protected bölümünde yer alır, çift olarak çalışır ve tabloya yeni satırlar eklenirken dahili olarak kullanılır.
Yeni bir satır oluşturan ve liste sonuna ekleyen metot:
//+------------------------------------------------------------------+ //| Создаёт новую строку и добавляет в конец списка | //+------------------------------------------------------------------+ CTableRow *CTableModel::RowAddNew(void) { //--- Создаём новую пустую строку и добавляем её в конец списка строк CTableRow *row=this.CreateNewEmptyRow(); if(row==NULL) return NULL; //--- Создаём ячейки по количеству ячеек первой строки for(uint i=0;i<this.CellsInRow(0);i++) row.CreateNewCell(0.0); row.ClearData(); //--- Успешно - возвращаем указатель на созданный объект return row; }
Bu public bir metottur. Tabloya hücreli yeni bir satır eklemek için kullanılır. Oluşturulan satır için hücre sayısı tablonun ilk satırından alınır.
Yeni bir satır oluşturan ve belirtilen liste konumuna ekleyen metot:
//+------------------------------------------------------------------+ //| Создаёт и добавляет новую строку в указанную позицию списка | //+------------------------------------------------------------------+ CTableRow *CTableModel::RowInsertNewTo(const uint index_to) { //--- Создаём новую пустую строку и добавляем её в конец списка строк CTableRow *row=this.CreateNewEmptyRow(); if(row==NULL) return NULL; //--- Создаём ячейки по количеству ячеек первой строки for(uint i=0;i<this.CellsInRow(0);i++) row.CreateNewCell(0.0); row.ClearData(); //--- Смещаем строку на позицию index_to this.RowMoveTo(this.m_list_rows.IndexOf(row),index_to); //--- Успешно - возвращаем указатель на созданный объект return row; }
Bazen satır listesinin sonuna değil, mevcut satırların arasına yeni bir satır eklememiz gerekir. Bu metot önce listenin sonunda yeni bir satır oluşturur, bu satırı hücrelerle doldurur, hücreleri temizler ve ardından satırı istenen konuma taşır.
Belirtilen hücreye bir değer ayarlayan metot:
//+------------------------------------------------------------------+ //| Устанавливает значение в указанную ячейку | //+------------------------------------------------------------------+ template<typename T> void CTableModel::CellSetValue(const uint row,const uint col,const T value) { //--- Получаем ячейку по индексам строки и столбца CTableCell *cell=this.GetCell(row,col); if(cell==NULL) return; //--- Получаем корректный тип устанавливаемых данных (double, long, datetime, color, string) ENUM_DATATYPE type=this.GetCorrectDatatype(typename(T)); //--- В зависимости от типа данных вызываем соответствующий типу данных //--- метод ячейки для установки значения, явно указывая требуемый тип switch(type) { case TYPE_DOUBLE : cell.SetValue((double)value); break; case TYPE_LONG : cell.SetValue((long)value); break; case TYPE_DATETIME: cell.SetValue((datetime)value); break; case TYPE_COLOR : cell.SetValue((color)value); break; case TYPE_STRING : cell.SetValue((string)value); break; default : break; } }
İlk olarak, satır ve sütun koordinatlarına göre istenen hücrenin işaretçisini alırız ve ardından ona bir değer ayarlarız. Hücreye ayarlamak için metoda iletilen değer ne olursa olsun, metot ayarlama için yalnızca doğru türü seçecektir - double, long, datetime, color veya string.
Belirtilen hücreye verilerin görüntülenme hassasiyetini ayarlayan metot:
//+------------------------------------------------------------------+ //| Устанавливает точность отображения данных в указанную ячейку | //+------------------------------------------------------------------+ void CTableModel::CellSetDigits(const uint row,const uint col,const int digits) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.SetDigits(digits); }
Metot yalnızca reel değer türünü depolayan hücrelerle ilgilidir. Hücre tarafından görüntülenen değer için ondalık basamak sayısını belirtmek için kullanılır.
Belirtilen hücreye zaman görüntüleme bayraklarını ayarlayan metot:
//+------------------------------------------------------------------+ //| Устанавливает флаги отображения времени в указанную ячейку | //+------------------------------------------------------------------+ void CTableModel::CellSetTimeFlags(const uint row,const uint col,const uint flags) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.SetDatetimeFlags(flags); }
Yalnızca datetime değerlerini görüntüleyen hücrelerle ilgilidir. Hücre tarafından görüntülenen zaman biçimini ayarlar (TIME_DATE|TIME_MINUTES|TIME_SECONDS veya bunların kombinasyonlarından biri)
TIME_DATE bayrağı sonucu: "yyyy.mm.dd"
TIME_MINUTES bayrağı sonucu: "hh:mi"
TIME_SECONDS bayrağı sonucu: "hh:mi:ss"
Belirtilen hücreye renk adı görüntüleme bayrağını ayarlayan metot:
//+------------------------------------------------------------------+ //| Устанавливает флаг отображения имён цветов в указанную ячейку | //+------------------------------------------------------------------+ void CTableModel::CellSetColorNamesFlag(const uint row,const uint col,const bool flag) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.SetColorNameFlag(flag); }
Yalnızca color değerlerini görüntüleyen hücrelerle ilgilidir. Hücrede depolanan renk, renk tablosunda mevcutsa renk adının görüntülenmesi gerektiğini belirtir.
Bir hücreye nesne atayan metot:
//+------------------------------------------------------------------+ //| Назначает объект в ячейку | //+------------------------------------------------------------------+ void CTableModel::CellAssignObject(const uint row,const uint col,CObject *object) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.AssignObject(object); }
Hücre için atanmış nesneyi iptal eden metot:
//+------------------------------------------------------------------+ //| Отменяет назначение объекта в ячейке | //+------------------------------------------------------------------+ void CTableModel::CellUnassignObject(const uint row,const uint col) { //--- Получаем ячейку по индексам строки и столбца и //--- вызываем её соответствующий метод для установки значения CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.UnassignObject(); }
Yukarıda sunulan iki metot, bir hücreye bir nesne atamanıza veya atamayı kaldırmanıza olanak tanır. Nesne, CObject sınıfının bir alt nesnesi olmalıdır. Tablolarla ilgili makaleler bağlamında, bir nesne örneğin ENUM_OBJECT_TYPE numaralandırmasındaki bilinen nesneler listesinden biri olabilir. Şu anda listede sadece hücre, satır ve tablo modeli nesneleri bulunmaktadır. Bunların bir hücreye atanması anlamlı değildir. Ancak kontrollerin oluşturulacağı Görünüm bileşeni hakkında makaleler yazdıkça numaralandırma genişleyecektir. Örneğin “açılır liste” kontrolünü bir hücreye atamak uygun olacaktır.
Belirtilen hücreyi silen metot:
//+------------------------------------------------------------------+ //| Удаляет ячейку | //+------------------------------------------------------------------+ bool CTableModel::CellDelete(const uint row,const uint col) { //--- Получаем строку по индексу и возвращаем результат удаления ячейки из списка CTableRow *row_obj=this.GetRow(row); return(row_obj!=NULL ? row_obj.CellDelete(col) : false); }
Metot, satır nesnesini indeksine göre alır ve belirtilen hücreyi silmek için metodunu çağırır. Tek bir satırdaki tek bir hücre için bu metot henüz bir anlam ifade etmez, çünkü tablonun yalnızca bir satırındaki hücre sayısını azaltacaktır. Bu, hücrelerin komşu satırlarla senkronize olmamasına neden olacaktır. Şimdiye kadar, tablo yapısının bozulmaması için silinen hücrenin yanındaki hücrenin iki hücre boyutuna "genişletilmesinin" gerekli olduğu bu tür bir silme işlemi yoktur. Ancak bu metot, tablo sütunu silme metodunun bir parçası olarak kullanılır; burada tüm satırlardaki hücreler tablonun bütünlüğünü ihlal etmeden bir kerede silinir.
Bir tablo hücresini taşıma metodu:
//+------------------------------------------------------------------+ //| Перемещает ячейку | //+------------------------------------------------------------------+ bool CTableModel::CellMoveTo(const uint row,const uint cell_index,const uint index_to) { //--- Получаем строку по индексу и возвращаем результат перемещения ячейки на новую позицию CTableRow *row_obj=this.GetRow(row); return(row_obj!=NULL ? row_obj.CellMoveTo(cell_index,index_to) : false); }
Satır nesnesini indeksine göre alırız ve belirtilen hücreyi taşımak için metodunu çağırırız.
Belirtilen satırdaki hücre sayısını geri döndüren metot:
//+------------------------------------------------------------------+ //| Возвращает количество ячеек в указанной строке | //+------------------------------------------------------------------+ uint CTableModel::CellsInRow(const uint index) { CTableRow *row=this.GetRow(index); return(row!=NULL ? row.CellsTotal() : 0); }
Satırı indeksine göre alırız ve CellsTotal() satır metodunu çağırarak içindeki hücre sayısını geri döndürürüz.
Tablodaki hücre sayısını geri döndüren metot:
//+------------------------------------------------------------------+ //| Возвращает количество ячеек в таблице | //+------------------------------------------------------------------+ uint CTableModel::CellsTotal(void) { //--- подсчёт ячеек в цикле по строкам (медленно при большом количестве строк) uint res=0, total=this.RowsTotal(); for(int i=0; i<(int)total; i++) { CTableRow *row=this.GetRow(i); res+=(row!=NULL ? row.CellsTotal() : 0); } return res; }
Metot, tablonun tüm satırlarının üzerinden geçer ve her satırdaki hücre sayısını geri döndürülecek toplam sonuca ekler. Tabloda çok sayıda satır varsa, bu tür bir sayım yavaş olabilir. Tablodaki hücre sayısını etkileyen tüm metotlar oluşturulduktan sonra ve yalnızca hücre sayısı değiştiğinde sayım yaparız.
Belirtilen tablo hücresini geri döndüren metot:
//+------------------------------------------------------------------+ //| Возвращает указанную ячейку таблицы | //+------------------------------------------------------------------+ CTableCell *CTableModel::GetCell(const uint row,const uint col) { //--- Получаем строку по индексу row и возвращаем по индексу col ячейку строки CTableRow *row_obj=this.GetRow(row); return(row_obj!=NULL ? row_obj.GetCell(col) : NULL); }
Satır indeksine göre satırı alırız ve satır nesnesinin GetCell() metodunu kullanarak sütun indeksine göre hücre nesnesinin işaretçisini geri döndürürüz.
Hücre açıklamasını geri döndüren metot:
//+------------------------------------------------------------------+ //| Возвращает описание ячейки | //+------------------------------------------------------------------+ string CTableModel::CellDescription(const uint row,const uint col) { CTableCell *cell=this.GetCell(row,col); return(cell!=NULL ? cell.Description() : ""); }
Satırı indeksine göre alırız, satırdan hücreyi elde ederiz ve açıklamasını geri döndürürüz.
Hücre açıklamasını günlüğe yazdıran metot:
//+------------------------------------------------------------------+ //| Выводит в журнал описание ячейки | //+------------------------------------------------------------------+ void CTableModel::CellPrint(const uint row,const uint col) { //--- Получаем ячейку по индексу строки и колонки и возвращаем её описание CTableCell *cell=this.GetCell(row,col); if(cell!=NULL) cell.Print(); }
Satır ve sütun indekslerine göre hücrenin işaretçisini alırız ve hücre nesnesinin Print() metodunu kullanarak açıklamasını günlükte görüntüleriz.
Belirtilen satırı silen metot:
//+------------------------------------------------------------------+ //| Удаляет строку | //+------------------------------------------------------------------+ bool CTableModel::RowDelete(const uint index) { //--- Удаляем строку из списка по индексу if(!this.m_list_rows.Delete(index)) return false; //--- После удаления строки необходимо обновить все индексы всех ячеек таблицы this.CellsPositionUpdate(); return true; }
CList sınıfının Delete() metodunu kullanarak indeksine göre satır nesnesini listeden sileriz. Satır silindikten sonra, kalan satırların ve içindeki hücrelerin indeksleri gerçeğe uymaz ve CellsPositionUpdate() metodu kullanılarak ayarlanmaları gerekir.
Bir satırı belirtilen konuma taşıyan metot:
//+------------------------------------------------------------------+ //| Перемещает строку на указанную позицию | //+------------------------------------------------------------------+ bool CTableModel::RowMoveTo(const uint row_index,const uint index_to) { //--- Получаем строку по индексу, делая её текущей CTableRow *row=this.GetRow(row_index); //--- Перемещаем текущую строку на указанную позицию в списке if(row==NULL || !this.m_list_rows.MoveToIndex(index_to)) return false; //--- После перемещения строки необходимо обновить все индексы всех ячеек таблицы this.CellsPositionUpdate(); return true; }
CList sınıfında, birçok metot geçerli liste nesnesiyle çalışır. Gerekli satırın işaretçisini alırız, bunu geçerli satır haline getiririz ve CList sınıfının MoveToIndex() metodunu kullanarak gerekli konuma taşırız. Satırı yeni bir konuma taşıdıktan sonra, kalan satırlar için indeksleri güncellemek gerekir; bunu CellsPositionUpdate() metodunu kullanarak yaparız.
Tüm hücreler için satır ve sütun konumlarını ayarlayan metot:
//+------------------------------------------------------------------+ //| Устанавливает позиции строки и колонки всем ячейкам | //+------------------------------------------------------------------+ void CTableModel::CellsPositionUpdate(void) { //--- В цикле по списку строк for(int i=0;i<this.m_list_rows.Total();i++) { //--- получаем очередную строку CTableRow *row=this.GetRow(i); if(row==NULL) continue; //--- устанавливаем строке индекс, найденный методом IndexOf() списка row.SetIndex(this.m_list_rows.IndexOf(row)); //--- Обновляем индексы позиций ячеек строки row.CellsPositionUpdate(); } }
Tablodaki tüm satır listesinin üzerinden geçeriz, sonraki her satırı seçeriz ve CList sınıfının IndexOf() metodunu kullanarak bulunan doğru indeksi ayarlarız. Ardından, satırdaki her hücre için doğru indeksi ayarlayan CellsPositionUpdate() satır metodunu çağırırız.
Bir satırdaki tüm hücrelerin verilerini temizleyen metot:
//+------------------------------------------------------------------+ //| Очищает строку (только данные в ячйках) | //+------------------------------------------------------------------+ void CTableModel::RowResetData(const uint index) { //--- Получаем строку из списка и очищаем данные ячеек строки методом ClearData() CTableRow *row=this.GetRow(index); if(row!=NULL) row.ClearData(); }
Satırdaki her hücre "boş" bir değere sıfırlanır. Şimdilik, basitleştirme amacıyla, sayısal hücreler için boş değer sıfırdır, ancak sıfır da hücrede görüntülenmesi gereken bir değer olduğu için bu daha sonra değiştirilecektir. Ve bir değerin sıfırlanması, boş bir hücre alanının görüntülenmesi anlamına gelir.
Tablodaki tüm hücrelerin verilerini temizleyen metot:
//+------------------------------------------------------------------+ //| Очищает таблицу (данные всех ячеек) | //+------------------------------------------------------------------+ void CTableModel::ClearData(void) { //--- В цикле по всем строкам таблицы очищаем данные каждой строки for(uint i=0;i<this.RowsTotal();i++) this.RowResetData(i); }
Tablodaki tüm satırların üzerinden geçeriz ve her satır için yukarıda bahsedilen RowResetData() metodunu çağırırız.
Satır açıklamasını geri döndüren metot:
//+------------------------------------------------------------------+ //| Возвращает описание строки | //+------------------------------------------------------------------+ string CTableModel::RowDescription(const uint index) { //--- Получаем строку по индексу и возвращаем её описание CTableRow *row=this.GetRow(index); return(row!=NULL ? row.Description() : ""); }
İndeksine göre satırın işaretçisini alırız ve açıklamasını geri döndürürüz.
Satır açıklamasını günlüğe yazdıran metot:
//+------------------------------------------------------------------+ //| Выводит в журнал описание строки | //+------------------------------------------------------------------+ void CTableModel::RowPrint(const uint index,const bool detail) { CTableRow *row=this.GetRow(index); if(row!=NULL) row.Print(detail); }
İndeksine göre satırın işaretçisini alırız ve elde edilen nesnenin Print() metodunu çağırırız.
Bir tablo sütununu silen metot:
//+------------------------------------------------------------------+ //| Удаляет столбец | //+------------------------------------------------------------------+ bool CTableModel::ColumnDelete(const uint index) { bool res=true; for(uint i=0;i<this.RowsTotal();i++) { CTableRow *row=this.GetRow(i); if(row!=NULL) res &=row.CellDelete(index); } return res; }
Tablonun tüm satırları boyunca bir döngü içinde, her bir sonraki satırı alırız ve sütun indeksine göre içindeki gerekli hücreyi sileriz. Bu, tablodaki aynı sütun indekslerine sahip tüm hücreleri siler.
Bir tablo sütununu taşıyan metot:
//+------------------------------------------------------------------+ //| Перемещает столбец | //+------------------------------------------------------------------+ bool CTableModel::ColumnMoveTo(const uint col_index,const uint index_to) { bool res=true; for(uint i=0;i<this.RowsTotal();i++) { CTableRow *row=this.GetRow(i); if(row!=NULL) res &=row.CellMoveTo(col_index,index_to); } return res; }
Tablonun tüm satırları boyunca bir döngü içinde, her bir sonraki satırı alırız ve gerekli hücreyi yeni konuma taşırız. Bu, tablodaki aynı sütun indekslerine sahip tüm hücreleri taşır.
Sütun hücrelerinin verilerini temizleyen metot:
//+------------------------------------------------------------------+ //| Очищает данные столбца | //+------------------------------------------------------------------+ void CTableModel::ColumnResetData(const uint index) { //--- В цикле по всем строкам таблицы for(uint i=0;i<this.RowsTotal();i++) { //--- получаем из каждой строки ячейку с индексом столбца и очищаем её CTableCell *cell=this.GetCell(i, index); if(cell!=NULL) cell.ClearData(); } }
Tablonun tüm satırları boyunca bir döngü içinde, her bir sonraki satırı alırız ve gerekli hücredeki verileri temizleriz. Bu, tablodaki aynı sütun indekslerine sahip tüm hücreleri temizler.
Nesne açıklamasını geri döndüren metot:
//+------------------------------------------------------------------+ //| Возвращает описание объекта | //+------------------------------------------------------------------+ string CTableModel::Description(void) { return(::StringFormat("%s: Rows %u, Cells in row %u, Cells Total %u", TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.RowsTotal(),this.CellsInRow(0),this.CellsTotal())); }
Tablo modelinin bazı parametrelerinden şu formatta bir dizge oluşturulur ve geri döndürülür:
Table Model: Rows 4, Cells in row 4, Cells Total 16:
Nesne açıklamasını günlüğe yazdıran metot:
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта | //+------------------------------------------------------------------+ void CTableModel::Print(const bool detail) { //--- Выводим в журнал заголовок ::Print(this.Description()+(detail ? ":" : "")); //--- Если детализированное описание, if(detail) { //--- В цикле по всем строкам таблицы for(uint i=0; i<this.RowsTotal(); i++) { //--- получаем очередную строку и выводим в журнал её детализированное описание CTableRow *row=this.GetRow(i); if(row!=NULL) row.Print(true,false); } } }
Önce modelin açıklaması şeklinde başlık yazdırılır ve ardından, ayrıntılı çıktı bayrağı ayarlanmışsa, tablo modelinin tüm satırlarının ayrıntılı açıklamaları bir döngü içinde yazdırılır.
Nesne açıklamasını tablo biçiminde günlüğe yazdıran metot:
//+------------------------------------------------------------------+ //| Выводит в журнал описание объекта в табличном виде | //+------------------------------------------------------------------+ void CTableModel::PrintTable(const int cell_width=10) { //--- Получаем указатель на первую строку (индекс 0) CTableRow *row=this.GetRow(0); if(row==NULL) return; //--- По количеству ячеек первой строки таблицы создаём строку заголовка таблицы uint total=row.CellsTotal(); string head=" n/n"; string res=::StringFormat("|%*s |",cell_width,head); for(uint i=0;i<total;i++) { if(this.GetCell(0, i)==NULL) continue; string cell_idx=" Column "+(string)i; res+=::StringFormat("%*s |",cell_width,cell_idx); } //--- Выводим строку заголовка в журнал ::Print(res); //--- Пройдём в цикле по всем строкам таблицы и распечатаем их в табличном виде for(uint i=0;i<this.RowsTotal();i++) { CTableRow *row=this.GetRow(i); if(row!=NULL) row.Print(true,true,cell_width); } }
İlk olarak, tablonun ilk satırındaki hücre sayısına bağlı olarak, tablo sütunlarının adlarını içeren bir tablo başlığı oluştururuz ve günlüğe yazdırırız. Ardından, bir döngü içinde tablonun tüm satırlarının üzerinden geçeriz ve her birini tablo biçiminde yazdırırız.
Tablo modelini ortadan kaldıran metot:
//+------------------------------------------------------------------+ //| Уничтожает модель | //+------------------------------------------------------------------+ void CTableModel::Destroy(void) { //--- Очищаем список строк m_list_rows.Clear(); }
Tablo satırları listesi basitçe temizlenir ve tüm nesneler CList sınıfının Clear() metodu kullanılarak ortadan kaldırılır.
Tablo modelini bir dosyaya kaydetme metodu:
//+------------------------------------------------------------------+ //| Сохранение в файл | //+------------------------------------------------------------------+ bool CTableModel::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_rows.Save(file_handle)) return(false); //--- Успешно return true; }
Veri başlangıcı işaretini ve liste türünü kaydettikten sonra, CList sınıfının Save() metodunu kullanarak satır listesini bir dosyaya kaydederiz.
Bir dosyadan tablo modeli yükleme metodu:
//+------------------------------------------------------------------+ //| Загрузка из файла | //+------------------------------------------------------------------+ bool CTableModel::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_rows.Load(file_handle)) return(false); //--- Успешно return true; }
Veri başlangıcı işaretini ve liste türünü yükleyip kontrol ettikten sonra, makalenin başında bahsedilen CListObj sınıfının Load() metodunu kullanarak dosyadan satır listesini yükleriz.
Bir tablo modeli oluşturmak için tüm sınıflar hazırdır. Şimdi, modelin çalışmasını test etmek için bir komut dosyası yazalım.
Sonucun test edilmesi
Kodu aynı dosyaya yazmaya devam edelim. Örneğin long türünde iki boyutlu 4x4'lük bir dizi (her biri 4 hücreden oluşan 4 satır) oluşturacağımız bir komut dosyası yazalım. Ardından, tablo modelinin verilerini içine yazmak için bir dosya açalım ve verileri dosyadan tabloya yükleyelim. Bir tablo modeli oluşturalım ve bazı metotlarının çalışmasını kontrol edelim.
Tablo her değiştirildiğinde, tablo modelinin nasıl yazdırılacağını seçen TableModelPrint() fonksiyonunu kullanarak elde edilen sonucu günlüğe yazdıracağız. PRINT_AS_TABLE makrosunun değeri true ise, CTableModel sınıfının PrintTable() metodu kullanılarak, değer false ise aynı sınıfın Print() metodu kullanılarak günlüğe yazdırma yapılır.
Komut dosyasında bir tablo modeli oluşturalım, tablo biçiminde yazdıralım ve modeli bir dosyaya kaydedelim. Ardından satırlar ekleyelim, sütunlar silelim, düzenleme iznini değiştirelim...
Sonrasında, tablonun orijinal halini dosyadan tekrar yükleyelim ve sonucu yazdıralım.
#define PRINT_AS_TABLE true // Распечатывать модель как таблицу //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Объявляем и заполняем массив с размерностью 4x4 //--- Тип массива может быть double, long, datetime, color, string long array[4][4]={{ 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12}, {13, 14, 15, 16}}; //--- Создаём модель таблицы из вышесозданного long-массива array 4x4 CTableModel *tm=new CTableModel(array); //--- Если модель не создана - уходим if(tm==NULL) return; //--- Распечатаем модель в табличном виде Print("The table model has been successfully created:"); tm.PrintTable(); //--- Удалим объект модели таблицы delete tm; }
Böylece, günlükte komut dosyasının aşağıdaki sonucunu alırız:
The table model has been successfully created: | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 9 | 10 | 11 | 12 | | Row 3 | 13 | 14 | 15 | 16 |
Tablo modeliyle çalışmayı, satırlar ve sütunlar eklemeyi, silmeyi ve taşımayı ve dosyayla çalışmayı test etmek için komut dosyasını tamamlayalım:
#define PRINT_AS_TABLE true // Распечатывать модель как таблицу //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Объявляем и заполняем массив с размерностью 4x4 //--- Тип массива может быть double, long, datetime, color, string long array[4][4]={{ 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12}, {13, 14, 15, 16}}; //--- Создаём модель таблицы из вышесозданного long-массива array 4x4 CTableModel *tm=new CTableModel(array); //--- Если модель не создана - уходим if(tm==NULL) return; //--- Распечатаем модель в табличном виде Print("The table model has been successfully created:"); tm.PrintTable(); //--- Проверим работу с файлами и функционал модели таблицы //--- Открываем файл для записи в него данных модели таблицы int handle=FileOpen(MQLInfoString(MQL_PROGRAM_NAME)+".bin",FILE_READ|FILE_WRITE|FILE_BIN|FILE_COMMON); if(handle==INVALID_HANDLE) return; //--- Сохраним в файл оригинальную созданную таблицу if(tm.Save(handle)) Print("\nThe table model has been successfully saved to file."); //--- Теперь вставим в таблицу новую строку в позицию 2 //--- Получим последнюю ячейку созданной строки и сделаем её нередактируемой //--- Распечатаем в журнале изменённую модель таблицы if(tm.RowInsertNewTo(2)) { Print("\nInsert a new row at position 2 and set cell 3 to non-editable"); CTableCell *cell=tm.GetCell(2,3); if(cell!=NULL) cell.SetEditable(false); TableModelPrint(tm); } //--- Теперь удалим столбец таблицы с индексом 1 и //--- распечатаем в журнале полученную модель таблицы if(tm.ColumnDelete(1)) { Print("\nRemove column from position 1"); TableModelPrint(tm); } //--- При сохранении данных таблицы файловый указатель был смещён на последние записанные данные //--- Поставим указатель в начало файла, загрузим ранее сохранённую оригинальную таблицу и распечатаем её if(FileSeek(handle,0,SEEK_SET) && tm.Load(handle)) { Print("\nLoad the original table view from the file:"); TableModelPrint(tm); } //--- Закроем открытый файл и удалим объект модели таблицы FileClose(handle); delete tm; } //+------------------------------------------------------------------+ //| Распечатывает модель таблицы | //+------------------------------------------------------------------+ void TableModelPrint(CTableModel *tm) { if(PRINT_AS_TABLE) tm.PrintTable(); // Распечатать модель как таблицу else tm.Print(true); // Распечатать детализированные данные таблицы }
Günlükte aşağıdaki sonucu alırız:
The table model has been successfully created: | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 9 | 10 | 11 | 12 | | Row 3 | 13 | 14 | 15 | 16 | The table model has been successfully saved to file. Insert a new row at position 2 and set cell 3 to non-editable | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 0.00 | 0.00 | 0.00 | 0.00 | | Row 3 | 9 | 10 | 11 | 12 | | Row 4 | 13 | 14 | 15 | 16 | Remove column from position 1 | n/n | Column 0 | Column 1 | Column 2 | | Row 0 | 1 | 3 | 4 | | Row 1 | 5 | 7 | 8 | | Row 2 | 0.00 | 0.00 | 0.00 | | Row 3 | 9 | 11 | 12 | | Row 4 | 13 | 15 | 16 | Load the original table view from the file: | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 9 | 10 | 11 | 12 | | Row 3 | 13 | 14 | 15 | 16 |
Tablo modelindeki verilerin günlüğe tablo biçiminde olmayan çıktısını almak istiyorsanız, PRINT_AS_TABLE makrosunda false değerini ayarlayın:
#define PRINT_AS_TABLE false // Распечатывать модель как таблицу
Bu durumda, günlükte aşağıdaki çıktı görüntülenir:
The table model has been successfully created: | n/n | Column 0 | Column 1 | Column 2 | Column 3 | | Row 0 | 1 | 2 | 3 | 4 | | Row 1 | 5 | 6 | 7 | 8 | | Row 2 | 9 | 10 | 11 | 12 | | Row 3 | 13 | 14 | 15 | 16 | The table model has been successfully saved to file. Insert a new row at position 2 and set cell 3 to non-editable Table Model: Rows 5, Cells in row 4, Cells Total 20: Table Row: Position 0, Cells total: 4: Table Cell: Row 0, Col 0, Editable <long>Value: 1 Table Cell: Row 0, Col 1, Editable <long>Value: 2 Table Cell: Row 0, Col 2, Editable <long>Value: 3 Table Cell: Row 0, Col 3, Editable <long>Value: 4 Table Row: Position 1, Cells total: 4: Table Cell: Row 1, Col 0, Editable <long>Value: 5 Table Cell: Row 1, Col 1, Editable <long>Value: 6 Table Cell: Row 1, Col 2, Editable <long>Value: 7 Table Cell: Row 1, Col 3, Editable <long>Value: 8 Table Row: Position 2, Cells total: 4: Table Cell: Row 2, Col 0, Editable <double>Value: 0.00 Table Cell: Row 2, Col 1, Editable <double>Value: 0.00 Table Cell: Row 2, Col 2, Editable <double>Value: 0.00 Table Cell: Row 2, Col 3, Uneditable <double>Value: 0.00 Table Row: Position 3, Cells total: 4: Table Cell: Row 3, Col 0, Editable <long>Value: 9 Table Cell: Row 3, Col 1, Editable <long>Value: 10 Table Cell: Row 3, Col 2, Editable <long>Value: 11 Table Cell: Row 3, Col 3, Editable <long>Value: 12 Table Row: Position 4, Cells total: 4: Table Cell: Row 4, Col 0, Editable <long>Value: 13 Table Cell: Row 4, Col 1, Editable <long>Value: 14 Table Cell: Row 4, Col 2, Editable <long>Value: 15 Table Cell: Row 4, Col 3, Editable <long>Value: 16 Remove column from position 1 Table Model: Rows 5, Cells in row 3, Cells Total 15: Table Row: Position 0, Cells total: 3: Table Cell: Row 0, Col 0, Editable <long>Value: 1 Table Cell: Row 0, Col 1, Editable <long>Value: 3 Table Cell: Row 0, Col 2, Editable <long>Value: 4 Table Row: Position 1, Cells total: 3: Table Cell: Row 1, Col 0, Editable <long>Value: 5 Table Cell: Row 1, Col 1, Editable <long>Value: 7 Table Cell: Row 1, Col 2, Editable <long>Value: 8 Table Row: Position 2, Cells total: 3: Table Cell: Row 2, Col 0, Editable <double>Value: 0.00 Table Cell: Row 2, Col 1, Editable <double>Value: 0.00 Table Cell: Row 2, Col 2, Uneditable <double>Value: 0.00 Table Row: Position 3, Cells total: 3: Table Cell: Row 3, Col 0, Editable <long>Value: 9 Table Cell: Row 3, Col 1, Editable <long>Value: 11 Table Cell: Row 3, Col 2, Editable <long>Value: 12 Table Row: Position 4, Cells total: 3: Table Cell: Row 4, Col 0, Editable <long>Value: 13 Table Cell: Row 4, Col 1, Editable <long>Value: 15 Table Cell: Row 4, Col 2, Editable <long>Value: 16 Load the original table view from the file: Table Model: Rows 4, Cells in row 4, Cells Total 16: Table Row: Position 0, Cells total: 4: Table Cell: Row 0, Col 0, Editable <long>Value: 1 Table Cell: Row 0, Col 1, Editable <long>Value: 2 Table Cell: Row 0, Col 2, Editable <long>Value: 3 Table Cell: Row 0, Col 3, Editable <long>Value: 4 Table Row: Position 1, Cells total: 4: Table Cell: Row 1, Col 0, Editable <long>Value: 5 Table Cell: Row 1, Col 1, Editable <long>Value: 6 Table Cell: Row 1, Col 2, Editable <long>Value: 7 Table Cell: Row 1, Col 3, Editable <long>Value: 8 Table Row: Position 2, Cells total: 4: Table Cell: Row 2, Col 0, Editable <long>Value: 9 Table Cell: Row 2, Col 1, Editable <long>Value: 10 Table Cell: Row 2, Col 2, Editable <long>Value: 11 Table Cell: Row 2, Col 3, Editable <long>Value: 12 Table Row: Position 3, Cells total: 4: Table Cell: Row 3, Col 0, Editable <long>Value: 13 Table Cell: Row 3, Col 1, Editable <long>Value: 14 Table Cell: Row 3, Col 2, Editable <long>Value: 15 Table Cell: Row 3, Col 3, Editable <long>Value: 16
Bu çıktı daha fazla hata ayıklama bilgisi sağlar. Örneğin, burada bir hücreye düzenleme yasağı bayrağının ayarlanmasının program günlüğünde görüntülendiğini görüyoruz.
Belirtilen tüm işlevler beklendiği gibi çalışıyor; satırlar ekleyebiliyor, taşıyabiliyor, sütunları silebiliyor, hücre özelliklerini değiştirebiliyor ve dosyalarla çalışabiliyoruz.
Sonuç
Bu, iki boyutlu bir veri dizisinden tablo oluşturulmasına olanak tanıyan basit bir tablo modelinin ilk sürümüdür. Daha sonra, diğer özel tablo modellerini oluşturacağız, Görünüm tablo bileşenini geliştireceğiz ve ileride kullanıcı grafik arayüzünün kontrollerinden biri olarak tablo verileriyle tam teşekküllü çalışacağız.
İçerdiği sınıflarla birlikte bugün oluşturduğumuz komut dosyası makaleye eklenmiştir. Kendi kendinize çalışmak için indirebilirsiniz.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/17653
Uyarı: Bu materyallerin tüm hakları MetaQuotes Ltd.'a aittir. Bu materyallerin tamamen veya kısmen kopyalanması veya yeniden yazdırılması yasaktır.
Bu makale sitenin bir kullanıcısı tarafından yazılmıştır ve kendi kişisel görüşlerini yansıtmaktadır. MetaQuotes Ltd, sunulan bilgilerin doğruluğundan veya açıklanan çözümlerin, stratejilerin veya tavsiyelerin kullanımından kaynaklanan herhangi bir sonuçtan sorumlu değildir.
Yeni Raylara Adım Atın: MQL5'te Özel Göstergeler
MetaTrader 5'i kullanarak Python'da özel döviz paritesi formasyonları bulma
İşte Karışınızda Yeni MetaTrader 5 ve MQL5
MetaTrader 5'i kullanarak Python'da yüksek frekanslı arbitraj alım-satım sistemi
- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz
SomeObject'in bir sınıfı, bu SomeObject'in Load() yöntemi çağrılarak bir dosyadan yüklendiğinde, "kendimi gerçekten dosyadan okudum mu?" diye kontrol eder (sorduğunuz şey budur). Aksi takdirde, bir şeylerin yanlış gittiği anlamına gelir, bu nedenle daha fazla yüklemenin bir anlamı yoktur.
Burada sahip olduğum şey, bir dosyadan bir nesne türünü okuyan bir LİSTE (CListObj). Liste dosyada ne olduğunu (hangi nesne) bilmiyor. Ancak CreateElement() metodunda oluşturmak için bu nesne türünü bilmesi gerekir. Bu yüzden dosyadan yüklenen nesnenin türünü kontrol etmez. Sonuçta, bu yöntemde bir nesnenin değil, bir listenin türünü döndüren Type() ile bir karşılaştırma yapılacaktır.
Teşekkürler, anladım, anladım.
Okudum ve sonra tekrar okudum.
MVC'de bir "model "den başka bir şey değildir. Örneğin bazı ListStorage
Merak ediyorum. Bu şekilde python ve R veri çerçevelerinin bir benzerini elde etmek mümkün mü? Bunlar, farklı sütunların farklı türlerde (sınırlı bir tür kümesinden, ancak dize dahil) veri içerebileceği tablolardır.
Yapabilirsiniz. Bir tablonun farklı sütunlarından bahsediyorsak, açıklanan uygulamada tablonun her hücresi farklı bir veri türüne sahip olabilir.