Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Делфи _ База вопросов

Автор: sheka 7.02.2010 0:05

Мне нужно сделать тест. Базу вопросов хочу хранить как file of record. Проблема заключается в том, что в тесте должны быть картинки. Хотел сделать как поле записи. Но вот проблема: в файл не записывается ни поле :TImage ни :TPicture ни даже :TBitmap.
Как это можно осуществить?

Автор: volvo 7.02.2010 0:18

Цитата
Как это можно осуществить?
По разному можно. Например, выбросить из головы желание заниматься глупостями и изобретать базу самостоятельно. Берешь любую настоящую базу данных (тот же MSAccess) и делаешь с ней все, что хочешь...

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

Автор: sheka 7.02.2010 2:43

Мне это надо в Дельфях...

Можете набросать пример?

type
tq=record
p:tbitmap;
end;

var
q:tq;
f:file of tq;

begin
//
end;

Автор: volvo 7.02.2010 3:38

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

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

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

В общем, хочешь извращений - читай: http://rsdn.ru/article/delphi/serialization.xml и реализуй... Как надоест - скажешь, я помогу тебе организовать хранение в БД.

Автор: sheka 7.02.2010 4:19

Цитата
Так какая разница, сделаешь ты хранение в 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 7.02.2010 6:58

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

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

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

Вот тут лежит http://filekeeper.org/download/volvo877/train_video.avi, показывающее, как и в какой последовательности надо подключать базу к проекту на Дельфи (это было давно сделано, почти 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 7.02.2010 10:50

sheka, база данных - это правильное решение, с ним разберись обязательно. Но если программа нужна срочно, то не вижу смысла вообще хранить изображения в каких-то своих форматах. Создай директорию и храни изображения там. Можешь изменить расширения файлов, чтобы никто не догадался =). А в записи отведи место только под имя файла.

Автор: sheka 7.02.2010 18:03

Цитата
А на что ты рассчитывал попасть? На готовую программу?
Ну как минимум на пример smile.gif
Archon, именно в таком виде у меня есть.

Автор: Client 8.02.2010 0:11

а можно вопрос? smile.gif
как организовать фильтрацию данных? как можно тут использовать запросы?
например, хочу найти записи, значение поля 'fio' равно 'Иванов'. записей может быть несколько

Автор: volvo 8.02.2010 1:04

Фильтруй, кто тебе запрещает?

procedure TForm1.Button3Click(Sender: TObject);
var s: string;
begin
s := 'Иванов';
ADOTable1.Filtered := False;
ADOTable1.Filter := '[fio] = ' + QuotedStr(s);
ADOTable1.Filtered := True;
end;
Оставит тебе только Ивановых.

Автор: Client 8.02.2010 1:15

спасибо

Автор: Client 9.02.2010 17:07

что-то сразу не заметил

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 9.02.2010 17:20

Цитата
Что она делает?
Ничего особенного. Просто преобразует входную строку так, что она на выходе "обернута" апострофами. Кстати, в основном - именно для работ с БД, где требуется строковые параметры запросов заключать в апострофы...

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



Автор: Client 9.02.2010 17:30

хитро... раньше вставлял апостроф с помощью chr(), а теперь буду знать как лучше smile.gif smile.gif

Автор: sheka 10.02.2010 4:10

А как узнать индекс выделенной записи в DBGrid?
Есть ли для этого какая-то функция или надо в цикле проверять selected ?


Добавлено через 1 мин.
Что такое TQuery?

Автор: volvo 10.02.2010 4:45

Цитата
А как узнать индекс выделенной записи в DBGrid?
А можно глупый вопрос? "А зачем?"

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

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

Автор: sheka 10.02.2010 5:18

Цитата
"А зачем?"
Например, удалить выделенную запись из базы.

Также интересует еще вопрос с картинкой: я нашел как ее можно показывать в самом DBGridе, по моему там было с помощью отрисовки Bitmapа на canvasе клетки, но в таком случае возникает вопрос с подгонкой размера картинки под размер клетки. Поэтому думал сделать так: если выделена запись, то вывести ее значение поля OLE в Image, который где-то бы на форме находился.

Автор: volvo 10.02.2010 6:17

А для этого ни разу не надо знать номер строки... Любая операция в Гриде производится с текущей строкой. Для ее удаления можно применить:

  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 11.02.2010 0:33

Опять столкнулся с проблемой. Точнее с -ами)))

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

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


Автор: volvo 11.02.2010 0:58

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

Цитата
использовать поле МЕМО, вывод которого ДБГрид тоже не поддерживает. Т.е. его надо привязать к объекту мемо(тому который на форму кинуть можно).
Это твои фантазии... Точно так же, как и в любой другой контрол, в ячейку DBGrid-а можно вставить TMemo. Подробнее - здесь: http://www.delphikingdom.com/asp/viewitem.asp?catalogid=806#02

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

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

Выбирай...

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

Автор: sheka 11.02.2010 5:36

Цитата
Поле типа Memo
Я текстовым пользуюсь), чтобы красиво в таблице выводило.

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

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

Вот, что получилось: Прикрепленный файл  TestMaker.rar ( 333.38 килобайт ) Кол-во скачиваний: 446
Посмотрите, пожалуйста, не смотря на то, что там весь код Ваш)

Автор: volvo 11.02.2010 6:09

Цитата
1. Как можно сделать для TDBImage свойство Proportional ? такое возможно?
У меня в Д2009 присутствуют свойства Proportional и Stretch:
Прикрепленное изображение

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

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

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

Автор: sheka 16.02.2010 0:48

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

Откуда можно скачачть Д2009 ? Кстати, она на Виндовс 7 нормально работает, еще не проверяли?

Автор: Client 16.02.2010 0:54

ADOTable1.RecNo и ADOTable1.RecordCount - это текущая запись и количество записей. Этого может и хватит.

Цитата
на Виндовс 7 нормально работает
На 7 не знаю, на висте все нормально...

Автор: sheka 16.02.2010 1:00

Цитата(Client @ 15.02.2010 19:54) *
это текущая запись и количество записей.

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

Автор: volvo 16.02.2010 2:21

Цитата
Кстати, она на Виндовс 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 16.02.2010 2:37

http://www.cyberguru.ru/delphi-sources/db-access/programmnoe-szhatie-bazy-dannykh-access-ispolzuya-dao.html
Вот нашел как программно сжать бд. Только Делфи ругается на DAO2000, говорит, что такого модуля он не знает.

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

Автор: volvo 16.02.2010 4:15

Цитата
если мне для работы ДБГрид не нужен(таблица на форме)
Хм... Ну, обойдись без него, просто не клади его на форму. Никто не запрещает связывать тот же 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 5.03.2010 1:22

Почему при перемещении по DataSource информация в DBText1 не меняется???
Спасайте, пожалуйста. Завтра уже надо сдавать тест в полностью рабочем состоянии... wacko.gif



Прикрепленные файлы
Прикрепленный файл  новый.zip ( 522.28 килобайт ) Кол-во скачиваний: 207

Автор: Client 5.03.2010 1:39

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

Автор: sheka 5.03.2010 1:45

Цитата(Client @ 4.03.2010 20:39) *

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

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

Это в procedure NextQuestion;

Автор: Client 5.03.2010 2:03

Путь к базе поменял в 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 5.03.2010 2:09

Цитата
form1.DBText1. // вот ошибка
Забыл видимо))), но проблема не в этом.

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



Добавлено через 6 мин.
На ХР тоже не работает...

Автор: Client 5.03.2010 2:21

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

Автор: sheka 5.03.2010 2:22

Цитата(Client @ 4.03.2010 21:21) *

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

Вот и я о том же!!! Хотя раньше работало..
В Delphi 2009 тоже не работает.

Автор: Client 5.03.2010 2:30

создал заново проект, добавил твою базу, кинул на форму дбтекст и дбедит. Все нормально обновляется...

Добавлено через 7 мин.
если без DBText никуда, то попробуй добавить обычный Label и задавай ему значение поля для текущей записи. Почему DBText сам не обновляется даже нет идей...

Автор: sheka 5.03.2010 3:01

Я думал, что проект мог быть "битым". Такое у меня уже бывало, но мне лень было его переделывать заново...
Спасибо за указание! good.gif

Автор: sheka 16.03.2010 2:14

volvo, Client, большое спасибо! Программу написал, сдал!