Разработать локальную справочную информационную систему «МУЗЫКА» на основе БД jet и исполнимого файла созданного в СИ ШАРП. пользовательский интерфейс должен содержать: 1. оглавление справочной системы в виде дерева с раскрывающимися пунктами 2. контейнер с форматирующим текстом открывающихся по двойному щелчку на пункте оглавления 3. контейнеры для изображений открывающиеся вместе с текстом 4. средства поиска : по оглавлению по тексту по ключевым словам пользовательский режим должен переключаться в 2-х режимах режим редактирования(должна быть реализована возможность добавления, удаления, изменения пунктов оглавления; редактирование, форматирование текстов; вставка, удаление изображений;) режим пользователя(доступ к пунктам оглавления, доступен только просмотр) КОММЕНТАРИИ К КОДУ!! ПРИМЕР РАБОТЫ НА Delphi Структура базы данных «Музыка»- основана на пяти связанных между собой таблицах. Каждая таблица имеет определённое количество полей, в которых хранятся данные определённого типа и определённой сущности. Таблицы нужны для того, чтобы хранить в них данные. Они являются основным источником информации. Схема данных показана на рис.1. Рисунок 1 – Схема данных Как видно из рисунка я создал отдельную таблицу музыка, где будет храниться вся информация о музыке, отдельную таблицу для стран и жанров. Далее необходимо спроектировать данные таблицы Таблица 1 – Таблица «Countries» Название поля key country Countries Тип данных счетчик текстовый Правило целостности Primary key not null Not null Directors Тип данных счетчик текстовый Правило целостности Primary key not null Not null Genres Тип данных счетчик текстовый Правило целостности Primarykeynotnull Not null Таблица 2 – Таблица «Directors» Название поля key director Таблица 3 – Таблица «Genres» Название поля key genre Таблица 4 – Таблица «music» Название поля key tree_node name country director genre annotation image music Тип данных счетчик числовой текстовый числовой числовой числовой Поле объекта OLE Поле объекта OLE Правило целостности Primary key not null Factory key not null Not null Factory key not null Factory key not null Factory key not null Not null Not null Structure Тип данных счетчик числовой текстовый Правило целостности Primarykeynotnull Factory key not null Not null Таблица 5 – Таблица «Structure» Название поля key parent_node name 2 Разработка клиентской части 2.1 Установка соединения с Базой данных В данном проекте клиентская часть разрабатывается в среде Delphi. При создании нового проекта необходимо создать новый DataModule. На который первым делом помещается компонент ADOConnection, который служит для связи с базой данных. В разрабатываемом клиенте используется один компонент ADOConnection с именем ADOConnection1. Далее производим его настройку и соединение с базой данных: Дважды щелкаем по компоненту ADOConnection1, в появившемся окне (рисунок 1) нажимаем “Build”. Рисунок - Окно создания подключения к базе Далее во вкладке “Поставщик данных”, выбрать подключаемые данные: Microsoft Jet 4.0 OLE DB Provider (рисунок 2), затем на вкладке “Подключение” указываем путь к базе данных в формате “.mdb”, при необходимости ввести имя пользователя и пароль (рисунок 3). И не забудьте проверить соединение с базой данных для корректной работы программы. Рисунок - Окно создания строки подключения Рисунок - Закладка подключение Далее располагаем девять, по числу таблиц нашей базы данных, компонентов ADOTable. Каждому из компонентов в свойстве Connection присваиваем соединение с компонентом ADOConnection1, в свойстве TableName выбираем соответствующую таблицу и базы данных, в свойстве Name присваиваем значение имя таблицы. Далее для каждого компонента ADOTable добавляем компонент TDataSource (компонент нужный для установления связи между компонентами доступа к данным и визуальными компонентами для представления данных пользователю), которые в сою очередь через свойство DataSet связываем с компонентами ADOTable. После проделанных операций необходимо добавить компонент ADOQuery, который реализует данные, источником которых является одна или несколько таблиц базы данных. Состав и структура получаемых данных определяется запросом SQL (SELECT). Кроме формирования набора данных, используется для выполнения любых действий, предусмотренных реализацией SQL для той СУБД, с которой работает ADOQuery. Свойству Connection данного компонента присваиваем так же значение ADOConnectin1, а в свойстве SQL задаем запрос. После чего для данного компонента добавляем еще один компонент TDataSource, для которого свойству DataSet присваиваем значение соответствующего запроса. На данном этапе так же необходимо определить какие таблицы можно будет изменять, а какие нет. Для этого свойству ReadOnly необходимо задать значение True если таблицу редактировать нельзя, и False если редактировать можно. Таким образом, созданный нами DataModule1 будет выглядеть следующим образом: Рисунок - DataModule1 2.2 Создание клиентской части 2.2.1 Разработка форм Главная форма предназначена для основной работы со справочно-информационной системой «Музыка» (рисунок 5) Рисунок - Главная форма На данной форме можно просматривать список музыки, который есть в базе данных, осуществлять поиск (рисунок 6), редактировать информацию о музыких (рисунок 7), осуществлять сортировку по категориям, жанру, режиссеру, стране (рисунок 8), добавлять новый альбом (рисунок 9), удалять его (рисунок 10). Рисунок – Поиск музыки Рисунок – Редактирование музыки Рисунок – Сортировка музыки Рисунок – Создание музыки Рисунок – Удаление музыки Приложение А листинг программы unit MainUnit; private { Private declarations } public { Public declarations } procedure FillNodes; //Построение TreeView procedure EditKeyWords; //Редактирование ключквых слов procedure Editing(Param: Boolean); //Набор переключаемых параметров end; var MainForm: TMainForm; SPos: Integer; //Позиция курсора при поиске vCurrentTable: TADOTable; //Текущая таблица vCurrentSource: TDataSource; //Текущий источник данных vParentNode: TTreeNode; //Родительский узел vFindFlag: Boolean; //Флаг поиска (активен/неактивен) vSearchStr: String; //Поисковая строка vKey: Integer; //Значение ключевого поля для сортировки implementation uses DataUnit, ViewUnit, EditUnit; {$R *.dfm} //Жирный шрифт procedure TMainForm.aBoldExecute(Sender: TObject); begin if aBold.Checked then RichEdit1.SelAttributes.Style := RichEdit1.SelAttributes.Style + [fsBold] else RichEdit1.SelAttributes.Style := RichEdit1.SelAttributes.Style - [fsBold]; end; //Отмена редактирования/создания procedure TMainForm.aCancelExecute(Sender: TObject); begin DataModule1.musicTable.Cancel; Editing(false); RichEdit1.Lines.Assign(DataModule1.musicTable.FieldByName('annotation')); Image1.Picture.Assign(DataModule1.musicTable.FieldByName('image')); end; //Очистить RichEdit procedure TMainForm.aClearExecute(Sender: TObject); begin RichEdit1.Lines.Clear; end; //Очистить строку поиска и сбросить флаг procedure TMainForm.aClearSearchExecute(Sender: TObject); begin FindEdit.Clear; vFindFlag := false; FillNodes; end; //Копировать в буфер обмена procedure TMainForm.aCopyExecute(Sender: TObject); begin RichEdit1.CopyToClipboard; end; //Вырезать procedure TMainForm.aCutExecute(Sender: TObject); begin RichEdit1.CutToClipboard; end; //Удаление пункта procedure TMainForm.aDeleteNodeExecute(Sender: TObject); var i, vBuf: Integer; begin //Если пунктов нет, то выходим из процедуры if DataModule1.StructureTable.RecordCount = 0 then exit; //Если есть потомки, то выводим сообщение и выходим if TreeView1.Selected.HasChildren then begin MessageBox(Handle, PChar('Сначала удалите дочерние узлы'), PChar('Внимание!'), MB_ICONINFORMATION + MB_OK) + IDOK; exit; end; //Если выбрали отмену удаления, то выходим if (MessageBox(Handle, PChar('Удалить выбранный пункт?'), PChar('Удаление пункта'), MB_ICONINFORMATION + MB_OKCANCEL + MB_DEFBUTTON2) = IDCANCEL) then exit //иначе находим в таблице строку, соответствующую выбранному //пункту, удаляем ее и обновляем дерево else begin with DataModule1.StructureTable do begin Locate('key', Integer(TreeView1.Selected.Data), [loCaseInsensitive]); vBuf := FieldByName('parent_node').AsInteger; Delete; end; FillNodes; end; //После удаления ищем родителя удаленного пункта, выделяем его и //раскрываем список оставшихся потомков этого родителя for i := 0 to TreeView1.Items.Count - 1 do if Integer(TreeView1.Items[i].Data) = vBuf then begin TreeView1.Selected := TreeView1.Items[i]; TreeView1.Items[i].MakeVisible; TreeView1.Items[i].Expand(false); end; //Очищаем RichEdit и Image RichEdit1.Lines.Clear; aDelImageExecute(Sender); ImagePanel.Visible := false; end; //Удалить изображение procedure TMainForm.aDelImageExecute(Sender: TObject); begin Image1.Picture.Bitmap.Assign(nil); end; //Редактировать procedure TMainForm.aEditExecute(Sender: TObject); begin //Находим в таблице строку, соответствующую выбранному //пункту, и включаем режим редактирования with DataModule1.musicTable do begin Locate('tree_node', Integer(TreeView1.Selected.Data), [loCaseInsensitive]); Edit; end; Editing(true); end; //Закрыть программу procedure TMainForm.aExitExecute(Sender: TObject); begin MainForm.Close; end; //Поиск по тексту procedure TMainForm.aFindTextExecute(Sender: TObject); begin //Запоминаем позицию курсора SPos := RichEdit1.SelStart; with FindDialog1 do begin //Начальное значение - выделенный текст FindText := RichEdit1.SelText; Execute; end; end; //Выбор шрифта procedure TMainForm.aFontExecute(Sender: TObject); begin if FontDialog1.Execute then RichEdit1.SelAttributes.Assign(FontDialog1.Font); RichEdit1.SetFocus; end; //Курсив procedure TMainForm.aItalicExecute(Sender: TObject); begin if aItalic.Checked then RichEdit1.SelAttributes.Style := RichEdit1.SelAttributes.Style + [fsItalic] else RichEdit1.SelAttributes.Style := RichEdit1.SelAttributes.Style - [fsItalic]; end; //Загрузка картинки procedure TMainForm.aLoadImageExecute(Sender: TObject); var JpegIm: TJpegImage; BmpIm: TBitMap; begin OpenDialog1.Filter:='*.bmp, *.jpg, *. jpeg|*.bmp; *.jpg; *.jpeg'; if not OpenDialog1.Execute then exit; BmpIm := TBitMap.Create; //Если открываемая картинка имеет расширение *.bmp, //то загружаем ее в Image if ExtractFileExt(OpenDialog1.FileName) = '.bmp' then Image1.Picture.LoadFromFile(OpenDialog1.FileName) //Если картинка формата jpeg, то сначала преобразуем ее в bmp, //а потом загружаем в Image else begin JpegIm := TJpegImage.Create; JpegIm.LoadFromFile(OpenDialog1.FileName); BmpIm.Assign(JpegIm); Image1.Picture.Assign(BmpIm); JpegIm.Destroy; end; BmpIm.Destroy; end; //Создать procedure TMainForm.aNewExecute(Sender: TObject); begin //Если TreeView не содержит пунктов или имя пункта - пустая строка, //то напоминаем о необходимости создать/переименовать пункт перед //созданием записи в БД if TreeView1.Selected.Text = '' then begin MessageBox(Handle, PChar('Необходимо создать новый пункт'), PChar('Внимание!'), MB_ICONINFORMATION + MB_OK) + IDOK; exit; end; DataModule1.musicTable.Insert; Editing(true); aDelImageExecute(Sender); end; //Создание нового пункта procedure TMainForm.aNewNodeExecute(Sender: TObject); var nnName: String; i: Integer; begin //Если нажали отмену, то выходим if not InputQuery('Создание пункта', 'Введите заголовок', nnName) then exit //иначе заносим пункт в БД и обновляем дерево else begin with DataModule1.StructureTable do begin Insert; FieldByName('parent_node').AsInteger := Integer(TreeView1.Selected.Data); FieldByName('name').AsString := nnName; Post; end; FillNodes; end; //Разворачиваем дерево до созданного пункта и делаем выделяем этот пункт for i := 0 to TreeView1.Items.Count - 1 do if TreeView1.Items[i].Text = nnName then begin TreeView1.Selected := TreeView1.Items[i]; TreeView1.Items[i].MakeVisible; end; end; //Загрузка текста из файла procedure TMainForm.aOpenFileExecute(Sender: TObject); begin OpenDialog1.Filter:='Текстовые файлы|*.rtf; *.doc; *.txt'; if OpenDialog1.Execute then begin RichEdit1.Lines.LoadFromFile(OpenDialog1.FileName); end; end; //Вставить procedure TMainForm.aPasteExecute(Sender: TObject); begin RichEdit1.PasteFromClipboard; end; //Переименование пункта procedure TMainForm.aRenameNodeExecute(Sender: TObject); var rnName: String; begin //Если нажали отмену, то выходим if not InputQuery('Переименование', 'Введите новое имя', rnName) then exit //иначе ищем пункт в БД, переименовываем его и обновляем дерево else begin with DataModule1.StructureTable do begin Locate('key', Integer(TreeView1.Selected.Data), [loCaseInsensitive]); Edit; FieldByName('name').AsString := rnName; Post; end; FillNodes; end; end; //Сохранение в таблице music procedure TMainForm.aSaveExecute(Sender: TObject); var i, vBuf: Integer; begin if (CountryList.Text = '') or (GenreList.Text = '') or (DirectorList.Text = '') then begin MessageBox(Handle, PChar('Необходимо заполнить все поля'), PChar('Внимание!'), MB_ICONINFORMATION + MB_OK) ; exit; end; with DataModule1.musicTable do begin if Modified or RichEdit1.Modified then begin FieldByName('name').AsString := TreeView1.Selected.Text; FieldByName('tree_node').AsInteger := Integer(TreeView1.Selected.Data); FieldByName('annotation').Assign(RichEdit1.Lines); FieldByName('image').Assign(Image1.Picture); vBuf := FieldByName('tree_node').AsInteger; end; Post; end; aCancelExecute(Sender); ImagePanel.Visible := true; for i := 0 to TreeView1.Items.Count - 1 do if Integer(TreeView1.Items[i].Data) = vBuf then begin TreeView1.Items[i].Selected := true; TreeView1.Items[i].MakeVisible; end; end; //Выделить все procedure TMainForm.aSelectAllExecute(Sender: TObject); begin RichEdit1.SetFocus; RichEdit1.SelectAll; end; //Включение сортировки procedure TMainForm.aSortExecute(Sender: TObject); begin //Используем ту же форму, что и для редактирования ключевых слов with EditForm do begin DBGrid1.DataSource := vCurrentSource; Caption := vCurrentTable.Fields.Fields[1].DisplayLabel; //Отключая ненужные кнопки InsertBtn.Visible := false; DeleteBtn.Visible := false; ShowModal; end; end; //Подчеркнутый procedure TMainForm.aUnderlineExecute(Sender: TObject); begin if aUnderline.Checked then RichEdit1.SelAttributes.Style := RichEdit1.SelAttributes.Style + [fsUnderline] else RichEdit1.SelAttributes.Style := RichEdit1.SelAttributes.Style - [fsUnderline]; end; //Применение параметров при редактировании/просмотре procedure TMainForm.Editing(Param: Boolean); begin aOpenFile.Enabled := Param; aSave.Enabled := Param; aLoadImage.Enabled := Param; RichEdit1.ReadOnly := not Param; ImagePanel.Visible := Param; aCut.Enabled := Param; aPaste.Enabled := Param; aClear.Enabled := Param; aFindText.Enabled := Param; EditPanel.Visible := Param; aDelImage.Enabled := Param; end; //Вызов окна ключевых слов procedure TMainForm.EditKeyWords; begin with EditForm do begin DBGrid1.DataSource := vCurrentSource; Caption := vCurrentTable.Fields.Fields[1].DisplayLabel; ShowModal; end; end; //Строим TreeView при появлении главной формы procedure TMainForm.FormShow(Sender: TObject); begin FillNodes; RichEdit1.SelAttributes.Assign(RichEdit1.DefAttributes); end; //Открытие изображение для просмотра в оригинальном разрешении procedure TMainForm.Image1DblClick(Sender: TObject); begin //Если Image пуст, то выходим if Image1.Picture.Bitmap.Empty then exit; //иначе создаем форму для просмотра и загружаем в нее изображение //в натуральную величину ViewForm := TViewForm.Create(MainForm); with ViewForm do begin ViewImage.Picture.Assign(Image1.Picture); Position := poMainFormCenter; ShowModal; end; end; //Панель "Правка" procedure TMainForm.N16Click(Sender: TObject); begin if N16.Checked then ToolBar2.Visible := true else ToolBar1.Visible := false; end; //Панель "Формат" procedure TMainForm.N17Click(Sender: TObject); begin if N17.Checked then ToolBar3.Visible := true else ToolBar1.Visible := false; end; procedure TMainForm.N31Click(Sender: TObject); begin end; //Режим сортировки по категориям (полное дерево) procedure TMainForm.MMCategorySortClick(Sender: TObject); begin //Отмечаем пункт меню галкой MMCategorySort.Checked := true; //Очищаем строку поиска и сбрасываем флаг FindEdit.Clear; vFindFlag := false; FillNodes; aNewNode.Enabled := true; end; //Выбор источника данных и таблицы (страны) procedure TMainForm.MMCountryClick(Sender: TObject); begin vCurrentTable := DataModule1.CountryTable; vCurrentSource := DataModule1.CountrySource; EditKeyWords; end; //Сортировка по стране procedure TMainForm.MMCountrySortClick(Sender: TObject); begin MMCountrySort.Checked := true; FindEdit.Clear; vFindFlag := false; vCurrentTable := DataModule1.CountryTable; vCurrentSource := DataModule1.CountrySource; aSortExecute(Sender); FillNodes; aNewNode.Enabled := false; end; //Выбор источника данных и таблицы (режиссеры) procedure TMainForm.MMDirectorsClick(Sender: TObject); begin vCurrentTable := DataModule1.DirectorTable; vCurrentSource := DataModule1.DirectorSource; EditKeyWords; end; //Сортировка по режиссеру procedure TMainForm.MMDirectorSortClick(Sender: TObject); begin MMDirectorSort.Checked := true; FindEdit.Clear; vFindFlag := false; vCurrentTable := DataModule1.DirectorTable; vCurrentSource := DataModule1.DirectorSource; aSortExecute(Sender); FillNodes; aNewNode.Enabled := false; end; //Выбор источника данных и таблицы (жанры) procedure TMainForm.MMGenreClick(Sender: TObject); begin vCurrentTable := DataModule1.GenreTable; vCurrentSource := DataModule1.GenreSource; EditKeyWords; end; //Сортировка по жанру procedure TMainForm.MMGenreSortClick(Sender: TObject); begin MMGenreSort.Checked := true; FindEdit.Clear; vFindFlag := false; vCurrentTable := DataModule1.GenreTable; vCurrentSource := DataModule1.GenreSource; aSortExecute(Sender); FillNodes; aNewNode.Enabled := false; end; //Панель "Стандартная" procedure TMainForm.N5Click(Sender: TObject); begin if N5.Checked then ToolBar1.Visible := true else ToolBar1.Visible := false; end; //Процедура построения структуры дерева procedure TMainForm.FillNodes; var i: integer; vTable: TADOTable; vQuery: TADOQuery; vQueryString: String; begin vTable := DataModule1.StructureTable; vQuery := DataModule1.SortQuery; vTable.First; TreeView1.Items.Clear; //Создаем корневой пункт vParentNode := TreeView1.Items.AddObject(nil, vTable.FieldByName('name').AsString, Pointer(vTable.FieldByName('key').asInteger)); vTable.Next; //Если выбран режим сортировки "по категориям", то //строим полное дерево if (MMCategorySort.Checked) and (not vFindFlag) then begin //Проходим по всей таблице while not vTable.Eof do begin i := 0; //Пока i меньше числа пунктов TreeView while i < TreeView1.Items.Count do //ищем потомка i-го узла if TreeView1.Items.Item[i].Data = Pointer(vTable.FieldByName('parent_node').asInteger) then //и, найдя его, добавляем в TreeView begin TreeView1.Items.AddChildObject(TreeView1.Items.Item[i], vTable.FieldByName('name').AsString, Pointer(vTable.FieldByName('key').asInteger)); //Если потомков больше нет, то прерываем цикл и переходим //к следующему узлу break; end else Inc(i); vTable.Next; end; //Если выбран другой режим сортировки, то: end else begin //Смотрим какой пункт меню выбран (и активен ли поиск по строке) //и в соответствии с ним присваиваем значение переменной vQueryString if MMCountrySort.Checked and not vFindFlag then vQueryString := 'SELECT *' + 'FROM music ' + 'WHERE country=' + IntToStr(vKey); if MMGenreSort.Checked then vQueryString := 'SELECT *' + 'FROM music ' + 'WHERE genre=' + IntToStr(vKey); if MMDirectorSort.Checked then vQueryString := 'SELECT *' + 'FROM music ' + 'WHERE director=' + IntToStr(vKey); if vFindFlag and MMCategorySort.Checked then vQueryString := 'SELECT *' + 'FROM music ' + 'WHERE name LIKE ' + '''' + vSearchStr + ''''; if vFindFlag and MMCountrySort.Checked then vQueryString := 'SELECT *' + 'FROM music ' + 'WHERE name LIKE ' + '''' + vSearchStr + '''' + 'AND country=' + IntToStr(vKey); if vFindFlag and MMGenreSort.Checked then vQueryString := 'SELECT *' + 'FROM music ' + 'WHERE name LIKE ' + '''' + vSearchStr + '''' + 'AND genre=' + IntToStr(vKey); if vFindFlag and MMDirectorSort.Checked then vQueryString := 'SELECT *' + 'FROM music ' + 'WHERE name LIKE ' + '''' + vSearchStr + '''' + 'AND director=' + IntToStr(vKey); //Выполняем запрос vQuery.Active := false; vQuery.SQL.Text := vQueryString; vQuery.Active := true; vQuery.First; //и строим дерево, удовлетворяющее условиям while not vQuery.Eof do begin TreeView1.Items.AddChildObject(vParentNode, vQuery.FieldByName('name').AsString, Pointer(vQuery.FieldByName('tree_node').asInteger)); vQuery.Next; end; end; //Корневой пункт всегда развернут TreeView1.Items.Item[0].Expand(false); end; //Поиск по тексту procedure TMainForm.FindDialog1Find(Sender: TObject); begin with FindDialog1 do begin if frMatchCase in Options //Поиск с учетом регистра then RichEdit1.SelStart := Pos(FindText, Copy(RichEdit1.Lines.Text, SPos + 1, Length(RichEdit1.Lines.Text))) + SPos - 1 //Поиск без учета регистра else RichEdit1.SelStart := Pos(AnsiLowerCase(FindText), AnsiLowerCase(Copy(RichEdit1.Lines.Text, SPos + 1, Length(RichEdit1.Lines.Text)))) + SPos - 1; if RichEdit1.SelStart >= SPos then begin //Выделение найденного текста RichEdit1.SelLength := Length(FindText); //Изменение начальной позиции поиска SPos := RichEdit1.SelStart + RichEdit1.SelLength + 1; end else MessageBox(Handle, PChar('Текст "' + FindText + '" не найден.'), PChar('Внимание!'), MB_ICONINFORMATION ); end; RichEdit1.SetFocus; end; //Строка поиска procedure TMainForm.FindEditChange(Sender: TObject); begin vSearchStr := '%' + FindEdit.Text + '%'; if FindEdit.Text <> '' then vFindFlag := true else vFindFlag := false; FillNodes; end; //Выбор пункта TreeView procedure TMainForm.TreeView1Change(Sender: TObject; Node: TTreeNode); begin aCancelExecute(Sender); with DataModule1.musicTable do begin //При выделении пункта ицем связанную с ним запись в БД и, //если она существует, загружаем ее if Locate('tree_node', Integer(TreeView1.Selected.Data), [loCaseInsensitive]) then begin aEdit.Enabled := true; RichEdit1.Lines.Assign(FieldByName('annotation')); Image1.Picture.Assign(FieldByName('image')); ImagePanel.Visible := true; aFindText.Enabled := true; end //иначе очищаем RichEdit и Image от результатов предыдущего запроса else begin aEdit.Enabled := false; RichEdit1.Lines.Clear; aDelImageExecute(Sender); ImagePanel.Visible := false; end; end; end; //Редактирование по двойному клику procedure TMainForm.TreeView1DblClick(Sender: TObject); begin if TreeView1.Selected.HasChildren then exit; if DataModule1.musicTable.Locate('tree_node', Integer(TreeView1.Selected.Data), [loCaseInsensitive]) then aEditExecute(Sender) else aNewExecute(Sender); end; end.