Мне нужно сделать тест. Базу вопросов хочу хранить как 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)
Так какая разница, сделаешь ты хранение в 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;
При втором запуске (и закомментированом коде) выдает ошибку, а если раскомментировать, то все нормально Что такое АV?
Как надоест - скажешь, я помогу тебе организовать хранение в БД.
Допустим уже надоело. Прочитать - прочитаю, но времени катастрофически нет... в идеале програмку надо было еще месяц назад сдать... а так где-то неделька осталась...
Добавлено через 2 мин. Помогите) С чего надо начинать?
Как бы отмазаться я смогу - тест из текстового файла есть, но ведь это же не то.
volvo
7.02.2010 6:58
Цитата
то что я там заметил - меня особо не обрадовало: составление таблиц, сортировки по параметрам итд.. "На готовенькое" не попал.
А на что ты рассчитывал попасть? На готовую программу?
Чего там делать? Открываешь MS Access, создаешь одну таблицу, содержащую как минимум одно поле типа OLEObject - я назвал его image - (в нем будет храниться изображение. Если тебе надо еще что-то - создавай сразу и все остальное).
Все поля, которые нужны - создал? Сохраняешь БД, и выходишь из Access-а, вся дальнейшая работа - из Дельфи. Сначала надо связать базу с твоей программой. Для этого на форму положим компонент TADOConnection (из раздела DbGo, у меня Д2009, поэтому, извини, все дальнейшие места расположения компонентов я буду приводить именно из этой версии), компонент TADOTable (тоже DbGo), и TDataSource (из Data Access)...
Вот тут лежит Видео, около 2.5 Мб, показывающее, как и в какой последовательности надо подключать базу к проекту на Дельфи (это было давно сделано, почти 2 года назад, просто сейчас оно оказалось нужно )
Подключил все, 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;
Ну, для начала тебе этого должно хватить...
Цитата
Что такое АV?
Это Access Violation - запрет доступа.
Archon
7.02.2010 10:50
sheka, база данных - это правильное решение, с ним разберись обязательно. Но если программа нужна срочно, то не вижу смысла вообще хранить изображения в каких-то своих форматах. Создай директорию и храни изображения там. Можешь изменить расширения файлов, чтобы никто не догадался =). А в записи отведи место только под имя файла.
sheka
7.02.2010 18:03
Цитата
А на что ты рассчитывал попасть? На готовую программу?
Ну как минимум на пример Archon, именно в таком виде у меня есть.
Client
8.02.2010 0:11
а можно вопрос? как организовать фильтрацию данных? как можно тут использовать запросы? например, хочу найти записи, значение поля '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(), а теперь буду знать как лучше
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 на длину ругается
Поле типа Memo (описанное как Memo в Access-е) способно хранить до 64К текста... Ничего себе у тебя предложения...
Цитата
использовать поле МЕМО, вывод которого ДБГрид тоже не поддерживает. Т.е. его надо привязать к объекту мемо(тому который на форму кинуть можно).
Это твои фантазии... Точно так же, как и в любой другой контрол, в ячейку DBGrid-а можно вставить TMemo. Подробнее - здесь: НеОбычный TDBGrid
Итого, у тебя есть варианты: 1. Положить на форму TMemo и точно так же, как ты из базы вытягиваешь картинку в лежащий на форме TImage, в этот самый Memo сбрасывать текст из соотв. поля текущей записи. Это обычный текст, кстати... Просто
2. (предпочтительно) Воспользоваться TDBMemo, связать его с нужным полем в таблице, и за тебя это будет делать сам компонент (кстати, я прошляпил, надо было тебе и про TDBImage сразу сказать, точно так же - связал с полем и при перемещении по DBGrid-у изображение изменяется автоматически...) 3. Вставить Memo внутрь DBGrid-а.
Выбирай...
Какая у тебя Дельфи, скажи заодно, чтоб я хоть примерно знал, что есть в арсенале, а чего нету...
sheka
11.02.2010 5:36
Цитата
Поле типа Memo
Я текстовым пользуюсь), чтобы красиво в таблице выводило.
Хотя мне идея 3 понравилась больше всего, но почему-то 2я поолучилась, по-моему, красивее. Delphi 7
Вопросики: 1. Как можно сделать для TDBImage свойство Proportional ? такое возможно? 2. почему после удаления картинок из БД ее размер не уменьшается?
2. почему после удаления картинок из БД ее размер не уменьшается?
Чтобы уменьшить размер Access-базы, надо сделать из самого Access-а Tools->Database Utilities->Repair and Compact Database. По-моему в ДРКБ было описано, как это сделать прямо из Дельфи, но точно не могу утверждать...
Цитата
Delphi 7 ... Вот, что получилось:
Посмотреть могу только визуально, ибо в Д7 еще не было работы с Юникодом, а у меня установлена по умолчанию НЕ кириллица, так что я вижу кракозябры. Ну, главное чтоб тебе нравилось
Хотя я предпочитаю давать файлам, таблицам и полям названия на английском языке (в крайнем случае - латинскими буквами), а то знаешь, мало ли...
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...
И еще сразу же вопрос: если мне для работы ДБГрид не нужен(таблица на форме), то все равно его надо кинуть на форму и установить Visible:=False; или без него можно как-то обойтись(ADOTable1 или DataSource1 они же вроде выполняют функции свызывания с базой).
volvo
16.02.2010 4:15
Цитата
если мне для работы ДБГрид не нужен(таблица на форме)
Хм... Ну, обойдись без него, просто не клади его на форму. Никто не запрещает связывать тот же TDBImage напрямую с TDataSource. Грид - он только для отображения информации.
Цитата
Вот нашел как программно сжать бд.
Ух ты "А мужики-то не знают" (С), и по-старинке пользуются:
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 }
// Вызывать - так: 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 не меняется??? Спасайте, пожалуйста. Завтра уже надо сдавать тест в полностью рабочем состоянии...
Client
5.03.2010 1:39
проект не компилится, если закомментировать то не запускается... Как в ДАТАСУРСЕ перемещаешся? Или через table? Кстати, а пароль как узнать можно от базы?
sheka
5.03.2010 1:45
Цитата(Client @ 4.03.2010 20:39)
проект не компилится, если закомментировать то не запускается... Как в ДАТАСУРСЕ перемещаешся? Или через table? Кстати, а пароль как узнать можно от базы?
Пароль и имя пустые. Почему не компилится? Если пишет что базы нет - то это путь надо поменять. Как Volvo писал, только через ДатаРесурс
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
запустилось... хм, если добавить в твой проект DBEdit, то в нем инфа обновляется, а в DBText нет...
sheka
5.03.2010 2:22
Цитата(Client @ 4.03.2010 21:21)
запустилось... хм, если добавить в твой проект DBEdit, то в нем инфа обновляется, а в DBText нет...
Вот и я о том же!!! Хотя раньше работало.. В Delphi 2009 тоже не работает.
Client
5.03.2010 2:30
создал заново проект, добавил твою базу, кинул на форму дбтекст и дбедит. Все нормально обновляется...
Добавлено через 7 мин. если без DBText никуда, то попробуй добавить обычный Label и задавай ему значение поля для текущей записи. Почему DBText сам не обновляется даже нет идей...
sheka
5.03.2010 3:01
Я думал, что проект мог быть "битым". Такое у меня уже бывало, но мне лень было его переделывать заново... Спасибо за указание!
sheka
16.03.2010 2:14
volvo, Client, большое спасибо! Программу написал, сдал!
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.