• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

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

    Запись на курс до 25 апреля. Получить промодоступ ...

Компонент Timage

  • Автор темы liones
  • Дата начала
L

liones

Добрый день!
Вопрос такой: пользователь хочет менять рисунок на форме. На форму поместила timage и написала процедуру:
Код:
procedure TGlav.Image1DblClick(Sender: TObject);
begin
if Glav.OpenPictureDialog1.Execute then
begin
//загружаем выбранный рисунок
Glav.Image1.Picture.LoadFromFile(Glav.OpenPictureDialog1.FileName);
Glav.Image1.Show; //отображем рисунок на форме
end;
end;

НО после закрытия программы этот рисунок не сохраняется, т.е. при каждом открытии нужно снова выбирать рисунок.
Что еще нужно дописать чтобы исправить эту проблемку?
 
N

nayke

Добрый день!
Вопрос такой: пользователь хочет менять рисунок на форме. На форму поместила timage и написала процедуру:
Код:
procedure TGlav.Image1DblClick(Sender: TObject);
begin
if Glav.OpenPictureDialog1.Execute then
begin
//загружаем выбранный рисунок
Glav.Image1.Picture.LoadFromFile(Glav.OpenPictureDialog1.FileName);
Glav.Image1.Show; //отображем рисунок на форме
end;
end;

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

Можно копировать рисунок в папку с программой и подгружать при открытии.
 
S

sinkopa

Можно копировать рисунок в папку с программой и подгружать при открытии.
Хм... Ну можно конечно и так... Только вот придется заморачиваться на тему удалять ненужные файлы.
Пользователь может десяток другой рисунков сменить за время сеанса... так ведь? :)
А еще они могут быть разные каждый раз... ICO, BMP, WMF и пр...
Гораздо "элегантней" просто запомнить состояние Timage на выходе
Код:
procedure TGlav.FormClose(Sender: TObject; var Action: TCloseAction);
var
m: TMemoryStream;
stored: string;
begin
m := TMemoryStream.Create;
m.WriteComponent(Glav.Image1);
m.Position := 0;
stored := ChangeFileExt(Application.ExeName,'.store');
m.SaveToFile(stored);
m.Free;
end;
а на загрузке приложения его восстановить
Код:
procedure TGlav.FormCreate(Sender: TObject);
var
m: TMemoryStream;
stored: string;
begin
stored := ChangeFileExt(Application.ExeName,'.store');
if not FileExists(stored) then
Exit;

m := TMemoryStream.Create;
m.LoadFromFile(stored);
m.Position := 0;
m.ReadComponent(Glav.Image1);
m.Free;
end;
По моему так... :)
 
N

nayke

Хм... Ну можно конечно и так... Только вот придется заморачиваться на тему удалять ненужные файлы.
Пользователь может десяток другой рисунков сменить за время сеанса... так ведь? :)
А еще они могут быть разные каждый раз... ICO, BMP, WMF и пр...
Гораздо "элегантней" просто запомнить состояние Timage на выходе

Формат файла особо роли не играет. Фал можно удалять при выборе нового - тогда будет всегда 1 актуальный.
В этом запись на диск будет происходить только при смене темы оформления.
А в вашем случае запись будет происходить постоянно при закрытие формы. А если форма не главная? и открывается по 10 раз при работе в системе.
Или скажем пользователь открыл 2 формы с фоновым рисунком. На первой перевыбрал фон. Закрыл первую, а затем вторую - итог изменения не сохранятся.
 
S

sinkopa

Формат файла особо роли не играет. Фал можно удалять при выборе нового - тогда будет всегда 1 актуальный.
В этом запись на диск будет происходить только при смене темы оформления.
Уважаю людей упертых... даже заблуждающихся... :)
Формат файла как раз роль и играет...
Если Вы использовали в своих проектах TImage, то Вы знаете что свой внутренний тип (TGraphic) TImage рождает именно на основе расширения файла.
Т.е. нельзя обозвать "imgstored.BMP" в какой нибуть "imgstored.BIN" а потом загрузить TImage.
Или Вы предлагаете проверять на наличие файла ВСЕХ поддерживаемых расширений с именем "imgstored.*"?
Поэтому:
Допустим пользователь выбрал "image1.ico" а потом поменял на "image2.bmp".
Допустим Вы копируете их в папку с проектом, с именем "imgstored.*"
В "Вашем" случае необходимо:
1) Удалить "imgstored.ICO" и записать на его место "imgstored.BMP"
2) Запомнить (где нибудь) что на следующем запуске нужно загрузить "imgstored.BMP"
3) При запуске узнать (как нибудь) что нужно загрузить "imgstored.BMP" а не "JPG" какой нибудь...
А в вашем случае запись будет происходить постоянно при закрытие формы.
А если форма не главная? и открывается по 10 раз при работе в системе.
Или скажем пользователь открыл 2 формы с фоновым рисунком. На первой перевыбрал фон. Закрыл первую, а затем вторую - итог изменения не сохранятся.
Да хоть сто... :)
В "моем" случае, "ваши вопросы" решаются одним флагом.
Код:
var
ImgChanged: Boolean;
//...
if ImgChanged then
begin
//... сохраняем...
ImgChanged := False;
end;
Ну... или ( раз Вы все еще настаиваете :) ) действительно сохранить состояние (НЕ ФАЙЛ) TImage непосредственно в момент смены изображения.
Хотя... если Вы внимательно присмотритесь к коду поста родившего данную тему... то там речь шла о конкретной форме (переменная Glav) и конкретном TImage на этой форме.
 
N

nayke

Уважаю людей упертых... даже заблуждающихся... :)
Формат файла как раз роль и играет...
Если Вы использовали в своих проектах TImage, то Вы знаете что свой внутренний тип (TGraphic) TImage рождает именно на основе расширения файла.

Ну давайте считать 1:1)) решение наверное возможно и то и то.

Я, помнится, в delphi делал так:
- Формировал папку(например CSS). Либо хранил в БД.
- При запуске приложения подгружал графические файлы - по имени без учета расширения.
- Реестр файлов хранил в Файле с темой, Файлы подменял при смене темы или конкретного файла.

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

sinkopa

Ну давайте считать 1:1)) решение наверное возможно и то и то.

Я, помнится, в delphi делал так:
- Формировал папку(например CSS). Либо хранил в БД.
- При запуске приложения подгружал графические файлы - по имени без учета расширения.
- Реестр файлов хранил в Файле с темой, Файлы подменял при смене темы или конкретного файла.

Есть минусы есть плюсы, но
- дополнительная обработка не требуется(флаг - это хорошо, но что если таких картинок 50?),
- нет ограничений - в какой бы последовательности человек формы не закрывал, какие бы операции не проводил - конфликтов не возникает.
;) пусть будет ничья...
Я собственно и не собирался соревнование устраивать...
Просто есть в Delphi придуманные программистами Borland/Embarcadero "фичи", которые на первый взгляд неочевидны (если не читать исходники)...
К примеру, тот же самый ReadComponent, происходит неявно всегда когда рождается любая VCL форма (грузится из DFM)...
И уж если "большие дядьки" считают что это быстро и безопасно, то и я не вижу причин, почему нам (программистам) не использовать те же методики... :)

Вот, на вскидку усовершенствовал "мой" подход. Учел так сказать Ваши "замечания".
Получилось (на мой взгляд) более универсально и даже проще с точки зрения использования.
(1) Пишем класс (файл юнита "uStoreImageList.pas")
Код:
unit uStoreImageList;

interface

uses
SysUtils, Classes, Forms, ExtCtrls, Contnrs;

type
TImagesStore = class(TObjectList)
private
FStoreFile: string;
public
constructor Create(StoreFile: string);
{ Добавляет в StoreList произвольный TImage }
procedure AddToStoreList(Image: TImage); overload;
{ Добавляет в StoreList все TImage указанной формы }
procedure AddToStoreList(Form: TForm); overload;
{ Сохраняет StoreList в файл с именем FStoreFile }
procedure StoreImages;
{ Восстанавливает состояния всех TImage из StoreList из файла с именем FStoreFile }
procedure RestoreImages;
{ Более безопасный метод восстановления, но чуть чуть более медленный }
procedure SafeRestoreImages;
end;

implementation

{ TImagesStore }

constructor TImagesStore.Create(StoreFile: string);
begin
inherited Create(False);
FStoreFile := StoreFile;
end;

procedure TImagesStore.AddToStoreList(Image: TImage);
begin
if (IndexOf(Image) < 0) then // исключает дубликаты
inherited Add(Image);
end;

procedure TImagesStore.AddToStoreList(Form: TForm);
var
i: Integer;
img: TImage;
begin
for i := 0 to Form.ComponentCount-1 do
if (Form.Components[i] is TImage) then
begin
img := Form.Components[i] as TImage;
AddToStoreList(img);
end;
end;

procedure TImagesStore.RestoreImages;
var
m: TMemoryStream;
i: Integer;
img: TImage;
begin
if (Count < 1) or (not FileExists(FStoreFile)) then
Exit;

m := TMemoryStream.Create;
try
m.LoadFromFile(FStoreFile);
m.Position := 0;

for i := 0 to Count - 1 do
if (m.Position < m.Size) then
begin
img := TImage(Self[i]);
m.ReadComponent(img);
end;
finally
m.Free;
end;
end;

procedure TImagesStore.StoreImages;
var
m: TMemoryStream;
i: Integer;
img: TImage;
begin
if (Count < 1) then
Exit;
m := TMemoryStream.Create;
try
for i := 0 to Count - 1 do
begin
img := TImage(Self[i]);
m.WriteComponent(img);
end;
m.Position := 0;
m.SaveToFile(FStoreFile);
finally
m.Free;
end;
end;

procedure TImagesStore.SafeRestoreImages;
var
m: TMemoryStream;
i: Integer;
img: TImage;
pos: Integer;
begin
if (Count < 1) or (not FileExists(FStoreFile)) then
Exit;

m := TMemoryStream.Create;
try
m.LoadFromFile(FStoreFile);
m.Position := 0;
img := TImage.Create(nil);

while (m.Position < m.Size) do
begin
pos := m.Position;
m.ReadComponent(img);

for i := 0 to Count - 1 do
if (img.Name = TImage(Self[i]).Name) then
begin
m.Position := pos;
m.ReadComponent(TImage(Self[i]));
end;
end;
img.Free;
finally
m.Free;
end;
end;

end.

(2) Добавляем данный модуль в проект.
(3) Открываем в редакторе файл проекта (.DPR) и добавляем код
Код:
program MyTestApp;

uses
Forms, SysUtils,
uStoreImageList in 'uStoreImageList.pas', // (1) Добавили модуль

Unit1 in 'Unit1.pas' {Glav},
Unit2 in 'Unit2.pas' {Form2};

{$R *.res}
var
Ims : TImagesStore;  // (2) Добавили переменную

begin
Application.Initialize;
Application.CreateForm(TGlav, Glav);
Application.CreateForm(TForm2, Form2);

Ims := TImagesStore.Create(ChangeFileExt(Application.ExeName,'.store'));

// (3) Добавили Image1 с формы Glav
Ims.AddToStoreList(Glav.Image1);
// (4) Добавили ВСЕ 150 :-) картинок с формы Form2
Ims.AddToStoreList(Form2);

// (5) Восстановили состояние
Ims.SafeRestoreImages; //Ims.RestoreImages;
Application.Run;
// (6) Сохранили состояние
Ims.StoreImages;
Ims.Free;
end.
Вот... по моему так :)
 
N

nayke

Вот... по моему так ;)

Ну я при решении исходил скорее из прикладной логики кнопок.
Класс конечно не найду, но отличие в том, что скажем:

Есть кнопка Accept - она едина на всех формах.

В Вашем решении при наличии 20 форм она сохранится 20 раз(а у кнопки обычно еще и несколько состояний хранится).
Также собственно при замене вида этой кнопки на одной форме пришлось бы менять вручную на всех и переписывать весь файл темы.
Т.е. завершение приложения неоптимально.. и если скажем в момент записи программа завернется аварийно - потеряем весь интерфейс.

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

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

Таким образом
1. Запись и чтение локализовано на один элемент.
2. Какие элементы грузить можно раскидать на приложение в целом(для основных элементов), либо на создание конкретной формы(если ее все равно отрисовывать динамически или она вызывается редко)
3. И скажем при смене дизайна кнопки пользователь ждет, что будет задержка. А долгое завершение программы для него ошибка)

Вт как-то так)
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!