Ввод, сохранение в текстовый файл и визуализация данных в среде Lazarus (язык pascal)
В данной статье рассмотрим решение основных задач, встречаемых в практике при разработке прикладной программы.
Типовые задачи:
1. Ввести некие данные;
2. Посчитать период времени от начала измерений;
3. Визуализировать их в форме графика;
4. Сохранение в текстовый файл;
5. Загрузка созданного файла и использование этих данных.
Сделано в последней на данный момент версии Lazarus 2.0.8 (в других вервиях возможны незначительное изменения)
Задачей будет ввод одного числового параметра в форму, отображение его на графике в зависимости от текущего времени. Сохранение происходит при закрытии программы, а загрузка — при открытии.
Вначале необходимо создать пустой проект и настроить его в меню Проект -> Параметры проекта.
Сохранить проект в отдельной папке: Проект -> Сохранить проект как…
На форму поместить следующие элементы:
1. TLabeledEdit (из вкладки Additional)
2. TButton (из вкладки Standart)
3. TChаrt (из вкладки Chart)
Должно получиться так: (Специально не изменялись названия имен компонентов и текст внутри, хотя это рекомендуется в настоящем проекте)
Далее нужно создать серию для визуализации: Правой кнопкой мыши по графику -> Редактор диаграммы -> Добавить -> График
Теперь нужно создать обработку событий в исходном коде программы.
Понадобятся три процедуры:
1. Инициализации procedure TForm1.FormShow(Sender: TObject);
В инспекторе объектов выбрать Form1 -> вкладка События -> OnShow (нажать …) Функция автоматически создается средой
2. Деинициализация procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
В инспекторе объектов выбрать Form1 -> вкладка События -> OnClose (нажать …) Функция автоматически создается средой
3. Обработка ввода (нажатия на кнопку) procedure TForm1.Button1Click(Sender: TObject);
На форме совершить двойное нажатие на кнопку Button1
Получаем следующий шаблон в разделе implementation
implementation {$R *.lfm} { TForm1 } // Инициализация procedure TForm1.FormShow(Sender: TObject); begin end; // Деинициализация procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin end; // Обработка ввода procedure TForm1.Button1Click(Sender: TObject); begin end;
Задаем хранилище данных в объекте TStrings, для этого в разделе var дописываем объявление переменной Data:TStringList.
var Form1: TForm1; Data:TStringList; // Объект для хранения введенных данных в текстовом виде
Теперь необходимо принять протокол сохранения информации. Например, пусть в базе данных хранится дата и время ввода числа в текстовом виде, а через разделитель «;» — само число с плавающей точкой (но разделитель целой и дробной частей будет запятая).
Т.е. формат такой: ДД.ММ.ГГГГ ЧЧ.ММ.СС;ЧИСЛО
Программа будет преобразовывать дату и время в метку времени, затем в миллисекунды, а затем в минуты. Из каждого нового измерения будет вычитаться первое измерение. Таким образом, отсчет времени будет от нуля минут.
Задаем алгоритм инициализации:
// Инициализация procedure TForm1.FormShow(Sender: TObject); var i: integer; // Объявление локальной переменной целого типа x, y : Double; // Хранение текущих координат графика str, // Хранение текущей строки time, // Хранение текущего времени start: string; // Хранение начального времени begin Data := TStringList.Create; // Выделяется память под объект Data Chart1LineSeries1.Clear; // Очистка данных в графике (серии) if FileExists('data.csv') then // Если файл data.csv с данными существует begin // ... иначе фала data.csv нет (еще нет), а значит загружать ничего не надо Data.LoadFromFile('data.csv'); // Используем метод загрузки из файла объекта TStringList. Он заполнит объект строками из файла. // Отображение загруженных данных на графике for i:=0 to Data.Count-1 do // Проход в цикле по всем записям. Свойство Data.Count показывает число строк в списке. Т.к. первый элемент 0, то надо вычесть 1. begin // извлечение (парсинг) полей времени и числа из строки str := Data.Strings[i]; // Извлекаем текущую (i-тую) строку time := copy(str, 1, pos(';',str)-1); // Копирование символов от начала строки до знака ";" (т.е. вычитается 1) { Примечание: функция copy(str, start, col) - извлекает col символов из строки str, начиная с символа start функция pos(ch, str) - находит номер первого символа ch из строке str } delete(str,1,pos(';',str)); // удаление извлеченных символов вместе с разделителем ";" из строки str { Примечание: функция delete(str, start, col) - удаляет col символов из строки str, начиная с символа start функция pos(ch, str) - находит номер первого символа ch из строке str } y := StrToFloatDef(str, -1); // Преобразование в число остатка строки str { Примечание: Так как в строке str осталось только одно число, то можно не использовать функцию copy. Если требуется передать еще несколько полей, то можно вставить такую же конструкцию, как: par := StrToFloatDef(copy(str, 1, pos(';',str)-1), -1); delete(str,1,pos(';',str)); } if i = 0 then start := time; // Если это первое измерение, то записать время в переменную start x:=TimeStampToMSecs(DateTimeToTimeStamp(StrToDateTime(time))) - TimeStampToMSecs(DateTimeToTimeStamp(StrToDateTime(start))); // Расчет разницы текущего времени и начального тек. = тек. - нач. { Примечание: функция StrToDateTime переводит строку со временем в тип дата/время функция DateTimeToTimeStamp переводит переменную типа дата/время в спец. тип метки времени функция TimeStampToMSecs переводит переменную типа метка времени в миллисекунды } x := x /(1000*60); // Перевод из мс в минуты Chart1LineSeries1.AddXY(x,y); // Добавить координаты x, y в серию и отобразить их на графике end; end; end;
Задаем алгоритм деинициализации:
// Деинициализация procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin Data.SaveToFile('data.csv'); // Сохранение данных в файл data.csv FreeAndNil(Data); // Уничтожение объекта Data из памяти end;
Задаем алгоритм обработки ввода:
// Обработка ввода procedure TForm1.Button1Click(Sender: TObject); var x, y: Double; // Хранение текущих координат графика time: TDateTime; // Хранение текущего времени str, // Хранение текущей строки start: string; // Хранение начального времени begin y := StrToFloatDef(LabeledEdit1.Text, -1); // Конвертирование текста в поле LabeledEdit1 в число, если там некорректное число, то выдаст -1 if y = -1 then exit; // "Защита от дурака" Если пользователь ввел не корректное число, то ничего не делать и выйти из функции // Расчет периода времени от предыдущего измерения в минутах time := Now; // Запись текущего времени из системной переменной Now в переменную time start := DateTimeToStr(time); // Присваивание переменной старт текущего времени в текстовом виде. Конвертация происходит функцией DateTimeToStr. Если это не первое измерение, то переменную позже переписать. if Data.Count > 0 then // Если были предыдущие измерения, то ... begin // нужно рассчитать разницу по времени // извлечение (парсинг) полей времени и числа из строки str := Data.Strings[0]; // Извлекаем первую строку start := copy(str, 1, pos(';',str)-1); // Копирование части строки со временем s start. Здесь происходит её изменение, в случае не первого измерения. { Примечание: функция copy(str, start, col) - извлекает col символов из строки st, начиная с символа start функция pos(ch, st) - находит номер первого символа ch из строке st } end; Data.Add( DateTimeToStr(time)+';'+FloatToStr(y)); // Запись нового времени и введенного значения в виде строки с разделитетем ";" (согласно принятого протокола) x := TimeStampToMSecs(DateTimeToTimeStamp(time)) - TimeStampToMSecs(DateTimeToTimeStamp(StrToDateTime(start))); // Расчет разницы текущего времени и начального тек. = тек. - нач. { Примечание: функция DateTimeToTimeStamp переводит переменную типа дата/время в спец. тип метки времени функция TimeStampToMSecs переводит переменную типа метка времени в миллисекунды функция StrToDateTime переводит строку со временем в тип дата/время } x := x /(1000*60); // Перевод из мс в минуты Chart1LineSeries1.AddXY(x,y); // Добавление новых координат в серию и отображение графика end;
Проверяем программу:
После закрытия программы в папке появится файл data.csv, содержимое которого можно увидеть в Блокноте:
09.05.2020 12:12:54;1 09.05.2020 12:13:00;2 09.05.2020 12:13:03;3 09.05.2020 12:13:06;4 09.05.2020 12:13:08;5 09.05.2020 12:13:10;5 09.05.2020 12:13:12;1 09.05.2020 12:13:12;1 09.05.2020 12:13:16;1 09.05.2020 12:13:19;31 09.05.2020 12:13:21;3 09.05.2020 12:13:21;3 09.05.2020 12:13:26;2 09.05.2020 12:13:43;0,2
Здесь указывается дата, время с точностью до секунд и через разделитель ; введенное число. Обратите внимание, что целая и дробная часть числа разделяется запятой.
Его можно открыть в обычном текстовом редакторе (типа Блокнот) или в редакторе электронных таблиц (типа Excel).
При открытии программы, график должен опять появиться со введенными значениями!
Все работает!
В качестве дополнения:
Также может понадобиться удалять записи из базы (и графика).
Для этого можно добавить еще одну кнопку и поле ввода номера измерения , а также воспользоваться следующими функциями:
Data.Delete( i ); // удаляет строку с номером i (начиная от 0)
Chart1LineSeries1.Delete( i ); // удаляет i-тый элемент в серии на графике
StrToIntDef(Edit1.Text,-1); // Взять введенный текст из поля Edit1 и перевести его в целое число. Если это не число — возвратить -1 (для проверки ввода)
Для проверки выхода за диапазон, можно использовать:
Chart1LineSeries1.Count; // возвратит количество элементов в серии на графике.
Data.Count; // возвратит количество строк-записей
Можно удалять только последнюю запись и тогда поле для ввода не потребуется:
Data.Delete( Data.Count-1 );
Chart1LineSeries1.Delete( Chart1LineSeries1.Count-1 );
При возникновении вопросов, пишите в комментариях здесь или в сообществе ВК!
(с) Роман Исаков