Помощь - Поиск - Пользователи - Календарь
Полная версия: База вопросов
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Делфи
sheka
Мне нужно сделать тест. Базу вопросов хочу хранить как file of record. Проблема заключается в том, что в тесте должны быть картинки. Хотел сделать как поле записи. Но вот проблема: в файл не записывается ни поле :TImage ни :TPicture ни даже :TBitmap.
Как это можно осуществить?
volvo
Цитата
Как это можно осуществить?
По разному можно. Например, выбросить из головы желание заниматься глупостями и изобретать базу самостоятельно. Берешь любую настоящую базу данных (тот же MSAccess) и делаешь с ней все, что хочешь...

Можно забросить содержимое картинки в TMemoryStream, и из него уже записать в файл. Но тут возникает вопрос: а сколько ты места отвел для хранения картинки в том поле, где она будет храниться? А если не поместится - что делать будешь?
sheka
Мне это надо в Дельфях...

Можете набросать пример?
type
tq=record
p:tbitmap;
end;

var
q:tq;
f:file of tq;

begin
//
end;
volvo
Цитата
Мне это надо в Дельфях...
Чего это? У тебя ж файл, который типизированный, не будет прямо в твоем EXE-шнике храниться, правда? Так какая разница, сделаешь ты хранение в MDB, а интерфейс в Дельфи или хранение в своем file of record, и интерфейс в том же Дельфи?

Цитата
type
tq=record
p:tbitmap;
end;

не пойдет. TBitmap - это класс, сохранение которого тебе ничего не даст (ну, сохранил ты в файле адрес того участка, где хранилось изображение, или его палитра, и что? Перезапусти программу, изображения там уже нет, при попытке чтения оттуда в лучшем случае получишь AV)

В общем, хочешь извращений - читай: Сериализация объектов стандартными средствами Delphi и реализуй... Как надоест - скажешь, я помогу тебе организовать хранение в БД.
sheka
Цитата
Так какая разница, сделаешь ты хранение в MDB, а интерфейс в Дельфи или хранение в своем file of record, и интерфейс в том же Дельфи?

Я с базами данных вообще никогда не работал. Слышал, что так это можно осуществить, просмотрел книжки мельком, но то что я там заметил - меня особо не обрадовало: составление таблиц, сортировки по параметрам итд.. "На готовенькое" не попал. Думал, через файлы легче будет.
Цитата
не пойдет. TBitmap - это класс, сохранение которого тебе ничего не даст (ну, сохранил ты в файле адрес того участка, где хранилось изображение, или его палитра, и что? Перезапусти программу, изображения там уже нет, при попытке чтения оттуда в лучшем случае получишь AV)
Теперь я понял, почему вот эта программа:

var
q,w:TBitmap;
f:file of TBitmap;
begin
assignfile(f,'in');
{ rewrite(f);
q:=TBitmap.Create;
q.LoadFromFile('1.bmp');
write(f,q);
closefile(f);}
reset(f);
read(f,w);
closefile(f);
form1.Image1.Picture.Bitmap:=w;
end;

При втором запуске (и закомментированом коде) выдает ошибку, а если раскомментировать, то все нормально smile.gif
Что такое АV?
Как надоест - скажешь, я помогу тебе организовать хранение в БД.
Допустим уже надоело. Прочитать - прочитаю, но времени катастрофически нет... в идеале програмку надо было еще месяц назад сдать... а так где-то неделька осталась...

Добавлено через 2 мин.
Помогите) С чего надо начинать?

Как бы отмазаться я смогу - тест из текстового файла есть, но ведь это же не то.
volvo
Цитата
то что я там заметил - меня особо не обрадовало: составление таблиц, сортировки по параметрам итд.. "На готовенькое" не попал.
blink.gif А на что ты рассчитывал попасть? На готовую программу? smile.gif

Чего там делать? Открываешь MS Access, создаешь одну таблицу, содержащую как минимум одно поле типа OLEObject - я назвал его image - (в нем будет храниться изображение. Если тебе надо еще что-то - создавай сразу и все остальное).

Все поля, которые нужны - создал? Сохраняешь БД, и выходишь из Access-а, вся дальнейшая работа - из Дельфи. Сначала надо связать базу с твоей программой. Для этого на форму положим компонент TADOConnection (из раздела DbGo, у меня Д2009, поэтому, извини, все дальнейшие места расположения компонентов я буду приводить именно из этой версии), компонент TADOTable (тоже DbGo), и TDataSource (из Data Access)...

Вот тут лежит Видео, около 2.5 Мб, показывающее, как и в какой последовательности надо подключать базу к проекту на Дельфи (это было давно сделано, почти 2 года назад, просто сейчас оно оказалось нужно smile.gif )

Подключил все, DBGrid показывает пустые ячейки? Прекрасно... Теперь можно их заполнять. Чтоб занести текст - можно прямо использовать DBGrid (т.е., прямо в нем и печатать). Картинки - да пожалуйста:

// 1. Из TImage в текущую запись БД:
procedure TForm1.Button1Click(Sender: TObject);
var memStr: TMemoryStream;
begin
memStr := TMemoryStream.Create;
try
Image1.Picture.Graphic.SaveToStream(memStr);
memStr.Seek(0, soFromBeginning);
DBGrid1.DataSource.DataSet.Edit;
TBlobField(DBGrid1.DataSource.DataSet.FieldByName('image')).LoadFromStream(memStr);
DBGrid1.DataSource.DataSet.Post;
finally
memStr.Free;
end;
end;

// 2. Из текущей записи БД назад в TImage - легко:
procedure TForm1.Button2Click(Sender: TObject);
var
bStream: TADOBlobStream;
bm: TBitmap;
begin
if not DBGrid1.DataSource.DataSet.FieldByName('image').IsNull then
begin
bStream := TADOBlobStream.Create(TBlobField(DBGrid1.DataSource.DataSet.FieldByName('image')), bmRead);
try
bm := TBitmap.Create;
bm.LoadFromStream(bStream);
Image2.Picture.Bitmap.Assign(bm); // Это я для теста гружу в ДРУГОЙ Image
bm.Free;
finally
bStream.Free;
end;
end;
end;


Ну, для начала тебе этого должно хватить... smile.gif

Цитата
Что такое АV?
Это Access Violation - запрет доступа.
Archon
sheka, база данных - это правильное решение, с ним разберись обязательно. Но если программа нужна срочно, то не вижу смысла вообще хранить изображения в каких-то своих форматах. Создай директорию и храни изображения там. Можешь изменить расширения файлов, чтобы никто не догадался =). А в записи отведи место только под имя файла.
sheka
Цитата
А на что ты рассчитывал попасть? На готовую программу?
Ну как минимум на пример smile.gif
Archon, именно в таком виде у меня есть.
Client
а можно вопрос? smile.gif
как организовать фильтрацию данных? как можно тут использовать запросы?
например, хочу найти записи, значение поля 'fio' равно 'Иванов'. записей может быть несколько
volvo
Фильтруй, кто тебе запрещает?

procedure TForm1.Button3Click(Sender: TObject);
var s: string;
begin
s := 'Иванов';
ADOTable1.Filtered := False;
ADOTable1.Filter := '[fio] = ' + QuotedStr(s);
ADOTable1.Filtered := True;
end;
Оставит тебе только Ивановых.
Client
спасибо
Client
что-то сразу не заметил
QuotedStr(s);
Что она делает? тыкнул ctrl+mouse и нашел описание функции
function QuotedStr(const S: string): string;
var
I: Integer;
begin
Result := S;
for I := Length(Result) downto 1 do
if Result[I] = '''' then Insert('''', Result, I);
Result := '''' + Result + '''';
end;
сделал эксперимент
procedure TForm2.Button2Click(Sender: TObject);
begin
Button2.Caption:='''';
end;
В итоге есть только одн апостроф. Получается, что <''''> это и есть апостроф в строке?
VolvoGuest
Цитата
Что она делает?
Ничего особенного. Просто преобразует входную строку так, что она на выходе "обернута" апострофами. Кстати, в основном - именно для работ с БД, где требуется строковые параметры запросов заключать в апострофы...

Цитата
Получается, что <''''> это и есть апостроф в строке?
Да, внешние апострофы - это признак строки, а внутренние два - это один апостроф, который строка и содержит... Дублирование необходимо для того, чтобы не завершать строку:
Button2.Caption := 'test of 's: error'; // Первый же одиночный апостроф строку "сломал"


Client
хитро... раньше вставлял апостроф с помощью chr(), а теперь буду знать как лучше smile.gif smile.gif
sheka
А как узнать индекс выделенной записи в DBGrid?
Есть ли для этого какая-то функция или надо в цикле проверять selected ?


Добавлено через 1 мин.
Что такое TQuery?
volvo
Цитата
А как узнать индекс выделенной записи в DBGrid?
А можно глупый вопрос? "А зачем?"

Нет такой функции, и не рекомендую я тебе ее писать... Ибо DBGrid может находиться в режиме фильтрации, ты найдешь индекс выделенной строки, потом тебе захочется что-то с ним сделать, а фильтр отменится или изменится, вся индексация летит к чертям, что дальше?

Обходной путь, конечно, есть. В классе TDBGrid есть свойство Row, но оно скрыто от посторонних глаз. Можно его открыть. Добавляешь
type
TDBGrid = class(DBGrids.TDBGrid)
public // Теперь Row считается общим членом класса
property Row;
end;
перед описанием класса формы, и потом обращаешься к свойству Row там, где нужно получить номер текущей записи ДБГрида. Но учти, разработчики VCL совсем не просто так ее скрыли от посторонних "шаловливых ручек". Некоторые причины я тебе уже привел.
sheka
Цитата
"А зачем?"
Например, удалить выделенную запись из базы.

Также интересует еще вопрос с картинкой: я нашел как ее можно показывать в самом DBGridе, по моему там было с помощью отрисовки Bitmapа на canvasе клетки, но в таком случае возникает вопрос с подгонкой размера картинки под размер клетки. Поэтому думал сделать так: если выделена запись, то вывести ее значение поля OLE в Image, который где-то бы на форме находился.
volvo
А для этого ни разу не надо знать номер строки... Любая операция в Гриде производится с текущей строкой. Для ее удаления можно применить:
  DBGrid1.DataSource.DataSet.Delete;


Цитата
Поэтому думал сделать так: если выделена запись, то вывести ее значение поля OLE в Image, который где-то бы на форме находился.
Правильно думал... Я приводил код отображения картинки из БД в TImage... Если его повесить на событие DBGrid->DataSource->OnDataChange, это будет выполнять поставленную задачу:
procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);
var
bStream: TADOBlobStream;
bm: TBitmap;
begin
if not DBGrid1.DataSource.DataSet.FieldByName('image').IsNull then
begin
bStream := TADOBlobStream.Create(TBlobField(DBGrid1.DataSource.DataSet.FieldByName('image')), bmRead);
try
bm := TBitmap.Create;
bm.LoadFromStream(bStream);
Image2.Picture.Bitmap.Assign(bm);
bm.Free;
finally
bStream.Free;
end;
end
else Image2.Picture := nil; // поле image пустое... Очищаем
end;


Это тебе не самописная база, где все пришлось бы делать вручную, и отслеживать текущую запись, и ловить момент перехода по списку записей... В ADO уже все есть...
sheka
Опять столкнулся с проблемой. Точнее с -ами)))

У меня должен в базе храниться достаточно длинный текст (где-то 1-3 предложения), а он целиком не помещается в одну строку(MSAccess на длину ругается, но это ничего страшного, можна обойтись и этой длиной. проблема в неудобности обозрения информации) . Вычитал, что стандартно многострочным поле ДБГрида быть не может. Мои варианты решений проблемы:
1) т.к. времени очень мало - забить на создание редактора базы и пользоваться MSAccess в этих целях. Но как тогда загрузить картинку через него? Если ее загружаешь( или просто из папки перетаскиваешь в поле ОЛЕ, или рисуешь встроеным редактором), то делфи выдает ошибку при считывании этого поля.
2) использовать поле МЕМО, вывод которого ДБГрид тоже не поддерживает. Т.е. его надо привязать к объекту мемо(тому который на форму кинуть можно).

из 2) появляется еще один вопрос: как считывать информацию из полей ДБГрида? (тоже как-то через поток?):
а) просто текстового поля
б) поля МЕМО - для реализации 2-го пункта.

volvo
Цитата
а он целиком не помещается в одну строку(MSAccess на длину ругается
blink.gif Поле типа Memo (описанное как Memo в Access-е) способно хранить до 64К текста... Ничего себе у тебя предложения...

Цитата
использовать поле МЕМО, вывод которого ДБГрид тоже не поддерживает. Т.е. его надо привязать к объекту мемо(тому который на форму кинуть можно).
Это твои фантазии... Точно так же, как и в любой другой контрол, в ячейку DBGrid-а можно вставить TMemo. Подробнее - здесь: НеОбычный TDBGrid

Итого, у тебя есть варианты:
1. Положить на форму TMemo и точно так же, как ты из базы вытягиваешь картинку в лежащий на форме TImage, в этот самый Memo сбрасывать текст из соотв. поля текущей записи. Это обычный текст, кстати... Просто
Memo1.Text := DBGrid1.DataSource.DataSet.FieldByName('memo_field').AsString;

2. (предпочтительно) Воспользоваться TDBMemo, связать его с нужным полем в таблице, и за тебя это будет делать сам компонент (кстати, я прошляпил, надо было тебе и про TDBImage сразу сказать, точно так же - связал с полем и при перемещении по DBGrid-у изображение изменяется автоматически...)
3. Вставить Memo внутрь DBGrid-а.

Выбирай...

Какая у тебя Дельфи, скажи заодно, чтоб я хоть примерно знал, что есть в арсенале, а чего нету...
sheka
Цитата
Поле типа Memo
Я текстовым пользуюсь), чтобы красиво в таблице выводило.

Хотя мне идея 3 понравилась больше всего, но почему-то 2я поолучилась, по-моему, красивее.
Delphi 7

Вопросики:
1. Как можно сделать для TDBImage свойство Proportional ? такое возможно?
2. почему после удаления картинок из БД ее размер не уменьшается?

Вот, что получилось: Нажмите для просмотра прикрепленного файла Посмотрите, пожалуйста, не смотря на то, что там весь код Ваш)
volvo
Цитата
1. Как можно сделать для TDBImage свойство Proportional ? такое возможно?
У меня в Д2009 присутствуют свойства Proportional и Stretch:
Нажмите для просмотра прикрепленного файла

Цитата
2. почему после удаления картинок из БД ее размер не уменьшается?
Чтобы уменьшить размер Access-базы, надо сделать из самого Access-а Tools->Database Utilities->Repair and Compact Database. По-моему в ДРКБ было описано, как это сделать прямо из Дельфи, но точно не могу утверждать...

Цитата
Delphi 7
...
Вот, что получилось:
Посмотреть могу только визуально, ибо в Д7 еще не было работы с Юникодом, а у меня установлена по умолчанию НЕ кириллица, так что я вижу кракозябры. Ну, главное чтоб тебе нравилось smile.gif

Хотя я предпочитаю давать файлам, таблицам и полям названия на английском языке (в крайнем случае - латинскими буквами), а то знаешь, мало ли...
sheka
Надо бы вопросы выбирать из БД рандомно. Как это осуществить?
Как выделить запись я так и не нашел, тем более вы говорили что этого лучше не делать.

Откуда можно скачачть Д2009 ? Кстати, она на Виндовс 7 нормально работает, еще не проверяли?
Client
ADOTable1.RecNo и ADOTable1.RecordCount - это текущая запись и количество записей. Этого может и хватит.
Цитата
на Виндовс 7 нормально работает
На 7 не знаю, на висте все нормально...
sheka
Цитата(Client @ 15.02.2010 19:54) *
это текущая запись и количество записей.

Мне надо выбрать рандомно запись...
Вот, например, с массивом я бы это делал так m[random(RecordCount)] итд. А как с базой быть?
volvo
Цитата
Кстати, она на Виндовс 7 нормально работает, еще не проверяли?
Прекрасно работает и на Win7 тоже.

Цитата
Вот, например, с массивом я бы это делал так m[random(RecordCount)] итд. А как с базой быть?

procedure TForm1.btnRandomRecClick(Sender: TObject);
var Curr: integer;
begin
with DBGrid1.DataSource.DataSet do
begin
Curr := Random(RecordCount);
First;
MoveBy(Curr);
end;
end;
, только не забудь вызвать при инициализации формы Randomize, иначе будешь получать все время одну и ту же запись. Чтобы видеть выбранную строку, желательно в опциях ДБГрида установить gdRowSelect и dgAlwaysShowSelection...
sheka
http://www.cyberguru.ru/delphi-sources/db-...olzuya-dao.html
Вот нашел как программно сжать бд. Только Делфи ругается на DAO2000, говорит, что такого модуля он не знает.

И еще сразу же вопрос: если мне для работы ДБГрид не нужен(таблица на форме), то все равно его надо кинуть на форму и установить Visible:=False; или без него можно как-то обойтись(ADOTable1 или DataSource1 они же вроде выполняют функции свызывания с базой).
volvo
Цитата
если мне для работы ДБГрид не нужен(таблица на форме)
Хм... Ну, обойдись без него, просто не клади его на форму. Никто не запрещает связывать тот же TDBImage напрямую с TDataSource. Грид - он только для отображения информации.

Цитата
Вот нашел как программно сжать бд.
Ух ты smile.gif "А мужики-то не знают" (С), и по-старинке пользуются:
Uses ComObj; // Добавить если еще нет этого модуля

// ...

function CompactAndRepair(DB: string) : boolean;
var
v: OLEvariant;
begin { CompactAndRepair }
Result := True;
try
v := CreateOLEObject('JRO.JetEngine');
try
//try
v.CompactDatabase('Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' + DB,
'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' + DB +
'x;Jet OLEDB:Engine type=5');
DeleteFile(DB);
RenameFile(DB + 'x', DB)
finally
v := Unassigned
end; { try }

except
Result := False
end; { try }
end; { CompactAndRepair }

// Вызывать - так:
procedure TForm1.btnCompactDBClick(Sender: TObject);
const sDBPath = 'F:\Programs\Delphi\myProject\base.mdb';
begin
ADOTable1.Close; // Закрываем все открытые таблицы и саму базу...
ADOConnection1.Close;

if CompactAndRepair(sDBPath) then
ShowMessage('Success')
else
ShowMessage('Fault');

ADOConnection1.Open; //Открываем в обратном порядке, сначала базу, потом - таблицы
ADOTable1.Open;
end;
, только для этого надо не устанавливать в Инспекторе Объектов ADOConnection.Connected в True, а делать это при инициализации формы, иначе база будет открыта в монопольном режиме, и сжатие не будет произведено. То же самое касается и ADOTable - они в Design-Time должны быть неактивны, активизировать их надо будет при запуске:
procedure TForm1.FormCreate(Sender: TObject);
begin
ADOConnection1.Open;
ADOTable1.Open;

// Ну, и что там тебе еще надо было...
end;
sheka
Почему при перемещении по DataSource информация в DBText1 не меняется???
Спасайте, пожалуйста. Завтра уже надо сдавать тест в полностью рабочем состоянии... wacko.gif
Client
проект не компилится, если закомментировать то не запускается...
Как в ДАТАСУРСЕ перемещаешся? Или через table?
Кстати, а пароль как узнать можно от базы?
sheka
Цитата(Client @ 4.03.2010 20:39) *

проект не компилится, если закомментировать то не запускается...
Как в ДАТАСУРСЕ перемещаешся? Или через table?
Кстати, а пароль как узнать можно от базы?

Пароль и имя пустые. Почему не компилится? Если пишет что базы нет - то это путь надо поменять.
Как Volvo писал, только через ДатаРесурс
form1.DataSource1.DataSet.First;
form1.DataSource1.DataSet.MoveBy(ord(variation[1])-1);

Это в procedure NextQuestion;
Client
Путь к базе поменял в AdoConnection.
procedure NextQuestion;
var i:integer;
begin
form1.DataSource1.DataSet.First;
form1.DataSource1.DataSet.MoveBy(ord(variation[1])-1);
form1.DBText1. // вот ошибка
delete(variation,1,1);
DrawComboNew;
inc(curr);
form1.Memo1.Lines.Clear;
for i:=1 to length(variation) do
form1.Memo1.Lines.Add(inttostr(ord(variation[i])));
end;

Без этой строки такая ошибка (на скрине)
В моем проекте (для теста, база Access) данные DBText и DBEdit обновляются при перемещеним (и через датасурс и через AdoTable)
sheka
Цитата
form1.DBText1. // вот ошибка
Забыл видимо))), но проблема не в этом.

Комп глючный!!!
Раньше все нормально работало, а теперь та проблема, о которой я говорю. Сейчас на ХР попробую...



Добавлено через 6 мин.
На ХР тоже не работает...
Client
запустилось... smile.gif
хм, если добавить в твой проект DBEdit, то в нем инфа обновляется, а в DBText нет...
sheka
Цитата(Client @ 4.03.2010 21:21) *

запустилось... smile.gif
хм, если добавить в твой проект DBEdit, то в нем инфа обновляется, а в DBText нет...

Вот и я о том же!!! Хотя раньше работало..
В Delphi 2009 тоже не работает.
Client
создал заново проект, добавил твою базу, кинул на форму дбтекст и дбедит. Все нормально обновляется...

Добавлено через 7 мин.
если без DBText никуда, то попробуй добавить обычный Label и задавай ему значение поля для текущей записи. Почему DBText сам не обновляется даже нет идей...
sheka
Я думал, что проект мог быть "битым". Такое у меня уже бывало, но мне лень было его переделывать заново...
Спасибо за указание! good.gif
sheka
volvo, Client, большое спасибо! Программу написал, сдал!
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.