Форум по Delphi программированию

 



Вернуться   Форум по Delphi программированию > Все о Delphi > Разное
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 17.03.2018, 19:04
Dredfil Dredfil вне форума
Прохожий
 
Регистрация: 17.03.2018
Сообщения: 16
Версия Delphi: Delphi 10.2
Репутация: 10
По умолчанию Сохранение и загрузка записи с часто изменяемой структурой

Добрый день.
Мозгую над одной задачей, подумал, может у кого есть опыт в подобных вещах.
В приложении есть форма настройки выбранной учетной записи сервиса. На форме несколько контролов разного типа: чекбокс, текстовое поле, поле с выпадающим списком и др.
Есть запись, типа:
Код:
TConfig = record
  Name: string[30];
  Age: byte;
  Active: boolean;
  Obj: array[1..10] of byte;
end;
и переменная, типа
Код:
var
  Config: TConfig;
Изменение значений контролов на форме приводит к изменению переменной Config. При закрытии формы происходит сохранение этой записи на диск.
Запись TConfig в моем случае часто меняется и уже достигла более 100 элементов, в том числе массивов со своей структурой.
При добавлении нового элемента в структуру записи TConfig:
1. добавляется новый контрол на форму;
2. к этому контролу пишется процедура, которая записывает значение контрола в элемент записи TConfig.
3. в событие показа формы добавляется код, заполняющий значение контрола.
При этом есть недостаток - если сохранять запись в типизированный файл (File of TConfig), то при изменении структуры данные из старого файла не читаются. А если преобразовывать запись во что-то вроде XML или подобный формат, то надо еще править процедуры сохранения и загрузки данных.

Я попробовал реализовать задачу другим путем - создал свой класс, типа именованного списка, который хранит элементы в формате имя = значение + организовал поддержку иерархической структуры - к элементам добавил ссылку на дочерний элемент и ссылку на следующий элемент этого же уровня; сделал методы навигации по данным.
В итоге решилась масса проблем и открылись свои плюсы.
Пример записи и чтение данных из объекта-списка.
Код:
var
  Config: TDataBank;

Config['Name']:='Василий';
Config['Age']:=5;
s:=Config['obj:2.Count']; // 2 элемент массива obj, свойство Count

Для заполнения формы и для изменения данных при манипуляции с контролами формы были созданы 2 универсальные процедуры, которые по имени контролов считывают и записывают данные в объекте-списке TDataBank. Поэтому исчезла необходимость под номером 2 и 3 случая использования записи.
Так же исчезла проблема с загрузкой: данные считываются по имени, а если какого-то элемента не хватает, то он просто не заполнен. При этом не требуется менять методы сохранения и загрузки.
Появилась возможность манипулировать структурой прям в runtime - при присвоении значения по неизвестному имени элемент с этим именем тут же создается в объекте-списке, в том числе массив или дочерний элемент.

Вроде все здорово, но появилась своя проблема - в процессе разработки приходится все переменные задавать как строки (Config['Name']) и если была допущена опечатка, то ошибку можно долго искать - IDE не подсказывает что название некорректное и не предлагает варианты названий переменных структуры когда поставишь точку после Config...
Приходится работать с текстовым файликом, где хранить описание всех элементов создаваемой структуры.

Есть ли у вас какая-либо практика или идеи как можно успешнее решить задачу при условии частого добавления или изменения структуры настроек?
Ответить с цитированием
  #2  
Старый 17.03.2018, 20:14
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 137
Версия Delphi: Delphi7
Репутация: 10
По умолчанию

А не проще ли использовать готовые компоненты, предназначенные для сохранения/чтения параметров?
Ответить с цитированием
  #3  
Старый 17.03.2018, 20:50
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,026
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Не, правда, зачем такие сложности, можно упростить задачу. Вот когда-то уже отвечал на подобное примером, скопируйте из него к себе содержимое FormCreate и FormDestroy, а дальше попробуйте изменить состояние/содержимое компонентов и перезапустите сборку, мож так проще будет
Вложения
Тип файла: zip iniForm.zip (11.2 Кбайт, 3 просмотров)
Ответить с цитированием
Этот пользователь сказал Спасибо Alegun за это полезное сообщение:
Dredfil (17.03.2018)
  #4  
Старый 17.03.2018, 23:41
Dredfil Dredfil вне форума
Прохожий
 
Регистрация: 17.03.2018
Сообщения: 16
Версия Delphi: Delphi 10.2
Репутация: 10
По умолчанию

Спасибо, посмотрю что там.
Ответить с цитированием
  #5  
Старый 18.03.2018, 00:08
Dredfil Dredfil вне форума
Прохожий
 
Регистрация: 17.03.2018
Сообщения: 16
Версия Delphi: Delphi 10.2
Репутация: 10
По умолчанию

Цитата:
Сообщение от Alegun
Не, правда, зачем такие сложности, можно упростить задачу. Вот когда-то уже отвечал на подобное примером, скопируйте из него к себе содержимое FormCreate и FormDestroy, а дальше попробуйте изменить состояние/содержимое компонентов и перезапустите сборку, мож так проще будет

Интересное решение. Оно подходит, чтобы сохранять и загружать свойства всех контролов формы.
Однако, если я пользователю вышлю обновленную программу с новым набором контролов, то у него при запуске подтянется старый ини-файл и затрет все новые контролы, которые я добавил на форму. А если я сам буду высылать ему новый ини-файл, то затрутся свойства контролов, настроенные пользователем. (когда более 100 элементов настройки и более 10 учетных записей перенастраивать очень затруднительно).
И вторая проблема это использование в логике программы настроек с этой формы, когда учетных записей 10 или 100. Сейчас при настройке всех учетных записей используется 1 форма: при открытии формы заполняются значения контролов настройками выбранной в данный момент учетной записи. Разве что, держать в памяти созданные формы для каждой учетной записи и для использования настроек конкретной учетной записи в логике программы обращаться к свойствам конкретой формы. Может быть, это решение и не плохое... Но все же остается первая проблема.

Последний раз редактировалось Dredfil, 18.03.2018 в 00:12.
Ответить с цитированием
  #6  
Старый 18.03.2018, 00:09
Dredfil Dredfil вне форума
Прохожий
 
Регистрация: 17.03.2018
Сообщения: 16
Версия Delphi: Delphi 10.2
Репутация: 10
По умолчанию

Цитата:
Сообщение от Guaho
А не проще ли использовать готовые компоненты, предназначенные для сохранения/чтения параметров?
Если таковые существуют, то наверняка проще. Можете привести примеры, а я посмотрю как можно применить к своей задаче?
Ответить с цитированием
  #7  
Старый 18.03.2018, 06:32
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,026
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Цитата:
Сообщение от Dredfil
...если я пользователю вышлю обновленную программу с новым набором контролов, то у него при запуске подтянется старый ини-файл и затрет все новые контролы, которые я добавил на форму...
Легко решается переименованием (cnf.~ini) при первом пуске новой версии
Цитата:
...А если я сам буду высылать ему новый ини-файл, то затрутся свойства контролов, настроенные пользователем...
Там же, при первом пуске анализируется наличие/состояние компонент, если оно отличается от дефолтных, их не трогать
Цитата:
...Разве что, держать в памяти созданные формы для каждой учетной записи и для использования настроек конкретной учетной записи в логике программы обращаться к свойствам конкретой формы...
Нет, форма останется одна, просто при смене учётки будет загружаться "образ" под конкретную запись

И видимо главное, здесь наблюдается смешивание понятий относящихся к самой программе и контентом ей создаваемым, такое нужно разносить в пространстве
Ответить с цитированием
  #8  
Старый 18.03.2018, 13:26
Dredfil Dredfil вне форума
Прохожий
 
Регистрация: 17.03.2018
Сообщения: 16
Версия Delphi: Delphi 10.2
Репутация: 10
По умолчанию

Цитата:
Сообщение от Alegun
Легко решается переименованием (cnf.~ini) при первом пуске новой версииТам же, при первом пуске анализируется наличие/состояние компонент, если оно отличается от дефолтных, их не трогатьНет, форма останется одна, просто при смене учётки будет загружаться "образ" под конкретную запись

И видимо главное, здесь наблюдается смешивание понятий относящихся к самой программе и контентом ей создаваемым, такое нужно разносить в пространстве

Спасибо за идею! Думаю, ее можно развить в нужном направлении.
Сервис работает в многопоточном режиме одновременно со всеми учетными записями: 1 поток - 1 учетка. Настройки, которые вводит пользователь влияют на логику работы потоков.

Я часто выпускаю обновления в виде изменения возможных настроек - это можно отнести к метаданным (набор возможных настроек). То, что заполняет пользователь, можно отнести к данным пользователя (состояние набора настроек). А то, чем оперирует сервис помимо настроек, можно отнести к данным сервиса.

Т.к. учетные записи не переключаются, а работают в разных потоках одновременно, придется все же создавать несколько экземпляров форм, полагаю.

Пока что не совсем понятно как именно при первом запуске обновленной программы сравнивать 2 ини файла (новый мною высланный и старый пользовательский), чтобы определить различия и из старого ини файла заполнить свойства контролов в новом.
Ответить с цитированием
  #9  
Старый 18.03.2018, 14:08
Аватар для dr. F.I.N.
dr. F.I.N. dr. F.I.N. вне форума
I Like it!
 
Регистрация: 12.12.2009
Адрес: Россия, г. Новосибирск
Сообщения: 647
Версия Delphi: D6/D7
Репутация: 26643
По умолчанию

Цитата:
Сообщение от Dredfil
Пока что не совсем понятно как именно при первом запуске обновленной программы сравнивать 2 ини файла (новый мною высланный и старый пользовательский), чтобы определить различия и из старого ини файла заполнить свойства контролов в новом.
Например, сделайте обязательную секцию с информацией о версии программы или версией файла конфигурации:
Код:
[ProgVer]
MetaVersion=0.1
__________________
Грамотно поставленный вопрос содержит не менее 50% ответа.
Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть.
Ответить с цитированием
Этот пользователь сказал Спасибо dr. F.I.N. за это полезное сообщение:
Dredfil (18.03.2018)
  #10  
Старый 18.03.2018, 14:17
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,026
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Цитата:
Сообщение от Dredfil
...Пока что не совсем понятно как именно при первом запуске обновленной программы сравнивать 2 ини файла (новый мною высланный и старый пользовательский), чтобы определить различия и из старого ини файла заполнить свойства контролов в новом.
А вы посмотрите текстовый файл, что образуется в промежутке работы связки из ObjectBinaryToText() > ObjectTextToBinary(), по своей сути, это обычный файл *.dfm, на этом этапе можно и сравнить
Ответить с цитированием
Этот пользователь сказал Спасибо Alegun за это полезное сообщение:
Dredfil (18.03.2018)
  #11  
Старый 18.03.2018, 17:31
Dredfil Dredfil вне форума
Прохожий
 
Регистрация: 17.03.2018
Сообщения: 16
Версия Delphi: Delphi 10.2
Репутация: 10
По умолчанию

Получается, надо парсить ини-файлы как обычный текст?
Алгоритм примерно таков: перебираем объекты в старом файле, находим соответствующий объект в новом файле, удаляем целиком секцию в новом файле, заменяя секцией из старого. Все, чего нет в старом файле, в новом останется нетронутым, а все чего нет в новом файле, но есть в старом, не перенесется, т.к. отныне оно не актуально.
Плюс создаем отдельные формы на каждую учетную запись и при работе с настройками просто показываем пользователю конкретную форму настроек, а в логике потока через переменную указатель (который заполняем при создании потока) обращаемся к нужной (своей) форме настроек.
Блестяще, коллеги! Низкий поклон всем, кто участвовал.
Ответить с цитированием
  #12  
Старый 18.03.2018, 20:32
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 137
Версия Delphi: Delphi7
Репутация: 10
По умолчанию

Цитата:
Сообщение от Dredfil
Если таковые существуют, то наверняка проще. Можете привести примеры, а я посмотрю как можно применить к своей задаче?
В библиотеках компонентов Rx и Ehlib (последних версий) есть такие. Думаю, что есть ещё варианты, если поискать.
Ответить с цитированием
  #13  
Старый 05.05.2018, 13:23
Dredfil Dredfil вне форума
Прохожий
 
Регистрация: 17.03.2018
Сообщения: 16
Версия Delphi: Delphi 10.2
Репутация: 10
По умолчанию

Все оказалось еще проще. Не надо выполнять парсинг в тексте.
Алгоритм такой:
1. Создаем временную форму и загружаем в нее форму из файла.
2. Перебираем все компоненты (через Components) на целевой форме и ищем их на временной форме по имени.
3. Если на временной форме компонент найден, то в зависимости от типа компонента, тянем нужные свойства на целевую форму.

вот и все ))

Всем удачи!
Ответить с цитированием
  #14  
Старый 16.10.2018, 14:41
Dredfil Dredfil вне форума
Прохожий
 
Регистрация: 17.03.2018
Сообщения: 16
Версия Delphi: Delphi 10.2
Репутация: 10
По умолчанию

Всем привет!
Давно реализовал алгоритм на основании обсуждений в форуме, но как оказалось, этот вариант не подходит.
Дело в том что учетных записей много и форма создается для каждой учетной записи. Когда их уже 100, то загрузка форм из файлов занимает чуть больше минуты. Но учеток может быть и 1000 и 2000. Выходит что при старте программы будет происходить загрузка в течение 10-20 минут. Обходной вариант - параллелить загрузку на потоки, но все равно не получится достичь приемлемого времени загрузки. До 5 секунд я считаю загрузка программы приемлемой. Плюс каждя форма жрет по 200 Кб памяти и это еще только менее 10% контролов на форме, которые в итоге планируется разместить.
Следовательно, нужно дальше думать, искать решение. Прошу подключиться, кому по силам.
Ответить с цитированием
  #15  
Старый 16.10.2018, 19:50
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 7,607
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

М-м-м... а зачем их, формы, создавать при старте? Создавай по мере необходимости. И убивай после закрытия, что бы память не жралась. Все-равно, все формы юзер одновременно открывать не будет. А если будет, то 0.5-1 сек на создание формы при ее открытии рояля не играют, юзер все равно на кнопки медленнее жмет.
Ответить с цитированием
Ответ



Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 01:34.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2020

ВКонтакте   Facebook   Twitter