Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Круглый стол
  
Правила КС
>> Настройки

Фильтр вопросов
>> Новые вопросы
отслеживать по
>> Новые ответы

Избранное

Страница вопросов
Поиск по КС


Специальные проекты:
>> К л ю к в а
>> Г о л о в о л о м к и

Вопрос №

Задать вопрос
Off-topic вопросы

Помощь

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  08:06[Войти] | [Зарегистрироваться]
Ответ на вопрос № 66788

24-11-2008 12:42
Здравствуйте.
Вопрос о настройках.
Программа имеет много настроек, они сгруппированы по нескольким вкладкам (к примеру, WinAmp, AIMP, QIP). Настройки сохраняются в INI-файле как только пользователь нажал "Сохранить" или "Применить".
А затем, в зависимости от настройки пользователя, программа определяет, как ей себя вести.
Вопрос вот в чём: как предпочтительней определять текущие настройки перед тем, как что-то сделать?
Приведу пример настройки: при подключении отображать имя пользователя в заголовке программы. Настройка типа флаг. И как после подключения определить эту настройку, чтоб знать, писать в заголовке формы имя пользователя или нет?

Я вижу несколько вариантов:
1) Сделать функцию, которая будет считывать из INI-файла текущую настройку каждый раз, когда понадобится узнать настройку;
2) При загрузке главной формы загружать в форму настроек все настройки из INI-файла и когда понадобится, считывать настройки из формы;
3) Сделать какой-нибудь глобальный массив настроек и при загрузке главной формы считать из INI-файла все настройки в этот массив, а когда понадобится - считать настройку из массива.

Лично я сейчас предпочитаю первый вариант - каждый раз обращаться к файлу.
А как поступают гуру? И другие смертные, вроде меня? ;)

Спасибо.

[+] Добавить в избранные вопросы

Отслеживать ответы на этот вопрос по RSS

Ответы:


Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице.
Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.

26-11-2008 00:29
Тут можно задуматься еще вот над чем:
Могут быть локальные настройки(для конкретного компьютера, или конкретного пользователя) и глобальные(для всей системы)
Соответственно, их надо разделить:
ini-файл с глоб.настройками положить где-то рядом с базой данных, чтобы к нему имели доступ все рабочие места.
С локальными настройками может быть 2 варианта:
1) Настройки на рабочее место. В этом случае ini-файл храниш где-то на локальном компе, например в рабочей папке программы
2) Настройки на пользователя, если пользователи могут менять рабочие места. В этом случае ini-файл локальных настроек надо хранить рядом с глобальными. В этом случае надо позаботиться об уникальности имен ini-файлов, например по имени пользователя в домене, или имени пользователя в твоей прикладной программе.

Значения настроек по умолчанию можно задать в программе, при объявлении, а потом, если есть соответствующий ini-файл, то обновить настройки из него, если его нет, то будут настройки по умолчанию.

25-11-2008 08:00 | Комментарий к предыдущим ответам
Приложение вполне может запрашивать в СУБД настройки по паре компьютер-пользователь, имитируя роаминг профайлы.

Да, при желании можно и локальные параметры запихнуть в БД, а извлекать по имени компьютера. Но на мой взгляд все-таки для этой цели отлично подходят ini-файлы и такое усложнение системы не дает никаких преимуществ.

25-11-2008 07:32
Я имел ввиду (см. ссылку), что есть настройки, которые в БД хранить невозможно, например
Хранить в СУБД можно всё, ибо это хранилище данных, за исключением строки соединения с этой СУБД. Но есть разные способы добыть эту строку в конечном приложении. Приложение вполне может запрашивать в СУБД настройки по паре компьютер-пользователь, имитируя роаминг профайлы. При уровне доверия к пользователю стемящемуся к 0 как-то "некрасиво" давать пользователю что-то кроме исполняемого файла. Но это касается больших систем.

25-11-2008 06:53
Я имел прежде всего свои задачи

А в таком виде оно и не вызывает принципиальных возражений.

Но первоначально это выглядело так, что настройки надо хранить только в базе данных и только так можно обеспечить разграничение прав, а других способов вообще не существует. Причем безотносительно того, работает ли программа с БД вообще, что, кстати, совсем не просматривается в вопросе.

Но в переформулированном виде Ваше высказывание выглядит уже вполне адекватно.

25-11-2008 06:26
И как интересно хранить настройки в такой системке ?

Я имел ввиду (см. ссылку), что есть настройки, которые в БД хранить невозможно, например:
- параметры коннекта к БД;
- размеры и положение окон программы;
- ширину столбцов в гридах;
- локальные пути для импорта/экспорта файлов;
и т.п.

25-11-2008 06:16
Не совсем согласен. Где хранить настройки - зависит от конкретной задачи.
Я имел прежде всего свои задачи.
Конкретная задача - распределенная система на 100+ пользователей без привязки пользователя к рабочему месту + условие, что часть настроек задается только админом программы, роуминг профили отсутствуют. И как интересно хранить настройки в такой системке ?
Это отнюдь не значит, что нельзя хранить настройки в файле, но сериализация настроек через поток, на чем в основном я настаиваю, позволяет сделать систему существенно гибче, тк можно хранить настройки в любом источнике, поддерживающем потоки, а это может быть как файл, так и СУБД.

25-11-2008 04:34
Без СУБД такое разделение настроек не обеспечить, потому сразу было решено отказаться от файлов.

Не совсем согласен. Где хранить настройки - зависит от конкретной задачи.
http://www.delphikingdom.com/asp/answer.asp?IDAnswer=48961

25-11-2008 04:20 | Комментарий к предыдущим ответам
min@y™
Очень интересная идея, спасибо за пример.

25-11-2008 02:37 | Сообщение от автора вопроса
min@y™
Спасибо большое за пример. Исходники высылать не нужно, благодарю. Думаю, мне будет полезнее самому написать подобное. Таким образом я лучше в этом разберусь.

25-11-2008 02:26
3) Третий вариант кажется наиболее подходящим. Однако, я предпочитаю использовать не массив, а переменную типа record, отражающую структуру ini-файла.

Я делаю немного сложнее, но в итоге добавлять новые настройки, хранить их в памяти, читать их оттуда, изменять и отображать на форме "Настройки" их гораздо легче. Настройки у меня хранятся в XML-файлеб но это не принципиально.
Опишу вкратце.

Каждая опция - это экземпляр класса, который является наследником вот такого общего абстрактного класса:


type
  // Тип селектора
  TSelectorType = (stUnknown,
                  stBoolean,
                  stInteger,
                  stColor,
                  stString,
                  stFont);

  // --------- Общий класс-родитель настроек -------------
  TCustomOption = class
  private
    FCaption: string;            // Название настройки, отображаемое на форме
    FName: string;                // Имя ноды в XML-файле <Setting>
    FSelectorType: TSelectorType; // Тип опции
    FGroupIndex: Integer;        // Идентификатор группы (для разделения)
    FVisisble: Boolean;          // Флаг вилимости опции. Если True, то под опцию создаётся контрол
  public
    constructor Create(const ACaption, AName: string); virtual;
    procedure SaveToXml(AParentNode: TXMLItem); virtual; abstract// Сохранение
    procedure LoadFromXml(ANode: TXMLItem); virtual; abstract;      // Загрузка
    property Name: string read FName;                                // Имя ноды в XML-файле <Setting>
    property Caption: string read FCaption;                          // Название настройки, отображаемое на форме
    property SelectorType: TSelectorType read FSelectorType write FSelectorType; // Тип опции
    property GroupIndex: Integer read FGroupIndex write FGroupIndex; // Идентификатор группы (для разделения)
    property Visible: Boolean read FVisisble write FVisisble;        // Флаг вилимости опции. Если True, то под опцию создаётся контрол
  end;



Вот классы - потомки:

 
type
  // ------- Класс для хранения Boolean ----------
  TBooleanOption = class(TCustomOption)
  private
    FValue: Boolean;  // Хранимое значение
  public
    procedure SaveToXml(AParentNode: TXMLItem); override// Сохранение
    procedure LoadFromXml(ANode: TXMLItem); override;      // Загрузка
    property Value: Boolean read FValue write FValue;      // Хранимое значение
  end;

  // ------- Класс для хранения Integer ----------
  TIntegerOption = class(TCustomOption)
  private
    FValue: Integer;  // Хранимое значение
  public
    procedure SaveToXml(AParentNode: TXMLItem); override// Сохранение
    procedure LoadFromXml(ANode: TXMLItem); override;      // Загрузка
    property Value: Integer read FValue write FValue;      // Хранимое значение
  end;

  // -------- Класс для хранения TColor -----------
  TColorOption = class(TCustomOption)
  private
    FValue: TColor;  // Хранимое значение
  public
    procedure SaveToXml(AParentNode: TXMLItem); override// Сохранение
    procedure LoadFromXml(ANode: TXMLItem); override;      // Загрузка
    property Value: TColor read FValue write FValue;      // Хранимое значение
  end;

  // ------- Класс для хранения string -------------
  TStringOption = class(TCustomOption)
  private
    FValue: string// Хранимое значение
  public
    procedure SaveToXml(AParentNode: TXMLItem); override// Сохранение
    procedure LoadFromXml(ANode: TXMLItem); override;      // Загрузка
    property Value: string read FValue write FValue;      // Хранимое значение
  end;

  // ------- Класс для хранения TFont -------------
  TFontOption = class(TCustomOption)
  private
    FValue: TFont;  // Хранимое значение
    FFontEffects: Boolean; // Флаг показа свойст шрифта: Подчёркнутый, зачёркнутый, цвет
  public
    constructor Create(const ACaption, AName: string); override;
    destructor Destroy(); override;
    procedure SaveToXml(AParentNode: TXMLItem); override// Сохранение
    procedure LoadFromXml(ANode: TXMLItem); override;      // Загрузка
    property Value: TFont read FValue;                    // Хранимое значение
    property FontEffects: Boolean read FFontEffects write FFontEffects;
  end;



Ну и наконец - сам класс-контейнер, в котором создаются, загружаются, изменяются, сохраняются и освобождаются настройки:

 
// ============= Класс - Настройки программы ================
  TProgramSettings = class
  private
    FOptions: TObjectList;            // Список настроек
    FFileName: string;                // Имя файла для хранения настроек
    function GetOptionsCount: Integer; // Количество настроек
    function GetOptionByIndex(const Index: Integer): TCustomOption;  // Получение опции по индексу
    function GetOptionByName(const AName: string): TCustomOption;    // Получение опции по имени ноды в XML-файле
    function GetOptionByCaption(const ACaption: string): TCustomOption; // Получение опции по названию, отображаемому на форме
  public
    constructor Create();
    destructor Destroy(); override;

    function NewOption(const ACaption, AName: string;            // Добавление опции в список
                      const ASelectorType: TSelectorType;
                      const AGroupIndex: Integer): TCustomOption;

    function SaveToXml(): Boolean;                                // Сохранение
    function LoadFromXml(): Boolean;                              // Загрузка

    property OptionsCount: Integer read GetOptionsCount;
    property OptionsByIndex[const Index: Integer]: TCustomOption read GetOptionByIndex; default; // опции по индексу
    property OptionsByName[const AName: string]: TCustomOption read GetOptionByName;            // опции по имени ноды в XML-файле
    property OptionsByCaption[const ACaption: string]: TCustomOption read GetOptionByCaption;    // опции по названию, отображаемому на форме
  end;



Вот его конструктор, где вручную прописано создание списка нужных настроек:

constructor TProgramSettings.Create;
var
  Index: Integer;
  Option: TCustomOption;
begin
  // Имя файла для хранения настроек
  FFileName:= ExtractFilePath(ParamStr(0)) + 'Settings.xml';

  // Создание объектов
  FOptions:= TObjectList.Create(True);

  // Добаляю группу "Цвета"
  for Index:= 0 to High(ColorSettingsNames) do
    begin
      Option:= NewOption(ColorSettingsNames[Index, 0], // Название (Caption)
                        ColorSettingsNames[Index, 1], // Имя (Name)
                        stColor,                      // Тип селектора
                        ColorSettingsGroup);          // Номер группы
      TColorOption(Option).Value:= ColorSettingsDefaults[Index];  // Значение по умолчанию
    end;

  // Добавляю группу "Шрифты"
  for Index:= 0 to High(FontSettingsNames) do
    begin
      Option:= NewOption(FontSettingsNames[Index, 0],  // Название (Caption)
                        FontSettingsNames[Index, 1],  // Имя (Name)
                        stFont,                        // Тип селектора
                        FontSettingsGroup);            // Номер группы

      TFontOption(Option).FontEffects:= FontEffectsVisible[Index]; // Флаги показа свойст шрифта                 
    end;

  // Добавляю группу "Интерфейс"
  for Index:= 0 to High(InterfaceSettingsNames) do
    begin
      Option:= NewOption(InterfaceSettingsNames[Index, 0],  // Название (Caption)
                        InterfaceSettingsNames[Index, 1],  // Имя (Name)
                        stBoolean,                          // Тип селектора
                        InterfaceSettingsGroup);            // Номер группы


      TBooleanOption(Option).Value:= InterfaceSettingsDefaults[Index]; // Значение по умолчанию
    end;

  // Создание невидимых опций
  NewOption('Имя последнего открытого файла',
            nLastFileName,
            stString,
            InvisibleGroup).Visible:= False; // имя последнего открытого файла
end;


При загрузке из файла/сохранении вызывается метод LoadFromXml/SaveToXml всех опций в списке.
Список опций висит в памяти и доступен для чтения записи.

Вот такова моя концепция. Она не является наставлением, как надо писать программы, просто здесь я показал, как пишу я. Если хочешь, я вышлю файл(ы) целиком - посмотришь.

24-11-2008 23:32
Утверждение столь же категоричное, сколь и не верное.
Хм... Может быть, но остальные варианты гораздо сложнее в реализации и администрировании (если речь идет о достаточно большой системе). Если хранить настройки в файловой системе, то необходимо обеспечить правильный доступ пользователя к файлам, что делает квалифицированый админ (обычно админ домена), поскольку это влияет на всю сеть в целом, нужна синхронизация файлов и тд и тп. С логированием действий пользователя - отдельная песня. В случае СУБД права изменения настроек определяются процедурами СУБД на основе прав записанных в таблицах СУБД (а идентификатор аутентифицированного пользователя при этом хранится в переменной пакета) и эти права применимы только к данной конкретной системе, а значит можно делегировать полномочия предоставления прав продвинутому юзеру (и логировать его действия, чтобы по башке настучать в случае чего, при этом можно однозначно утверждать, что действия выполнены сознательно именно этим пользователем). Аналогичную штуку можно реализовать в трехзвенке, но трехзвенка тяжелее в реализации и зачастую опять же использует СУБД для хранения данных. Больше вариантов мне в голову не приходит.
Можно, конечно, хранить все настройки в текстовом файле, доступном всем для записи, но инициативных дураков у нас очень много и найти потом концы кто, где и что поменял  не получится, тк никто не сознается, если за руку не поймали.

24-11-2008 23:02
а есть настройки, которые должны задаваться администратором системы (специфика системы). Без СУБД такое разделение настроек не обеспечить

Утверждение столь же категоричное, сколь и не верное.

24-11-2008 22:38
Лично я тупо использую EhLib + переопределение некоторых функций (для сохранения в СУБД) + глобальные переменные для  хранения глобальных настроек приложения. Что-то ближе к третьему варианту. Некоторая часть этой идеи изложена здесь http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1374
Приведу пример настройки: при подключении отображать имя пользователя в заголовке программы.
Согласно статье есть глобальный флаг при изменении которого вызовется обработчик главной формы и в нем, соответственно вызовется перерисовка заголовка, те настройка применится автоматически и немедленно, причем изменение настройки может быть произведено в одной форме, а это сразу же будет применено ко всем остальным формам, а условия применения настройки (фактически и список форм, на которые повлияет настройка) определяются конкретной формой.

Вообще, ИМХО, настройки через компонент должны сохраняться/читаться из потока, а не из файла. А вот что именно связано с потоком уже определяется приложением. Потом опять же есть настройки, которые может поменять любой пользователь, а есть настройки, которые должны задаваться администратором системы (специфика системы). Без СУБД такое разделение настроек не обеспечить, потому сразу было решено отказаться от файлов.

24-11-2008 14:16
3) Третий вариант кажется наиболее подходящим. Однако, я предпочитаю использовать не массив, а переменную типа record, отражающую структуру ini-файла.

Я лично так и делаю. Есть структура, есть 2 процедуры - чтения/записи настроек в/из файла. Тут есть один удобный бонус. Если пользователь нажал "Применить" - настройки сохраняются в структуру, но не в файл, и применяются. Если что-то не так, можно нажать "Отмена" - старые настройки считаются из файла, всё станет как было. А вот при нажатии "Сохранить" - уже пишутся в файл, откат невозможен.

Формы с настройками вообше предпочитаю убрать из Auto-create, т.к. используются они редко. И уж ни в коем случае не хранить в них настройки.

24-11-2008 14:11
вариант 3+1. при нажатии применить сбрасывать в файл, а до этого работать с памятью. чтение из файла только при загрузке программы

24-11-2008 13:16
1) Мне представляется первый вариант негибким. Он не учитывает возможности менять настройки в ходе исполнения и не сохранять эти временные настройки, а оставлять их действующими только в текущем сеансе.

2) Второй вариант тебует лишнего - зачем заводить форму только для того, чтобы там держать набор настроек?

3) Третий вариант кажется наиболее подходящим. Однако, я предпочитаю использовать не массив, а переменную типа record, отражающую структуру ini-файла.

Добавьте свое cообщение

Вашe имя:  [Войти]
Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
контрольный вопрос:
Вода мокрая или сухая?
в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.
Тип сообщения:
Текст:
Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве

    Страница избранных вопросов Круглого стола.
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования