Как сделать рамку в delphi

Добавил пользователь Владимир З.
Обновлено: 05.10.2024

Delphi 7. Занятие 2_5. Часть 2.

Продолжение. Начало -> Перейти

Интерфейс второй формы.

Как и прежде, решая основной вопрос, мы попутно будем знакомиться с новыми компонентами и функциями.

Для функционировании программы нам понадобятся компонент image delphi и компоненты MainMenu, CheckBox и Panel.

Для этого введём две глобальные переменные для хранения значений высоты и ширины панели:

var h_rem,w_rem:integer;

и расположим их в секции Interface.

В нём мы зададим и запомним начальные значения высоты и ширины панели на второй форме, после чего установим высоту и ширину панели:

procedure TForm2.FormCreate(Sender: TObject);

h_rem:=380;

w_rem:=630;

form2.Image1.Height:=h_rem;

form2.Image1.Width:=w_rem;

form2.Panel1.Color:=rgb(200,170,120); //функция rgb(r,g,b) устанавливает цвет по 3-м компонентам: r-красному, g-зелёному и b-синему. Каждый из них может принимать значение от 0 до 255.

Здесь мы также устанавливаем цвет панели с помощью функции rgb().

Если менять числа в скобках этой функции от 0 до 255, то получим всю возможную палитру цветов.

delphi image

delphi image

delphi image

delphi image

delphi image

площадь которого зальём зелёным цветом.

delphi image

В двух следующих пунктах указан размер картинки в пикселях.

В зависимости от состояния флага CheckBox1 будет меняться режим отображения.

При неустановленном флаге картинка будет показаны в своём естественном размере.

Обработчики событий пунктов меню.

Чтобы в delphi загрузить картинку в image, создадим следующие обработчики событий.

procedure TForm2.N4Click(Sender: TObject);

unit3.ImageShow(ss);

procedure TForm2.N5Click(Sender: TObject);

var ss:string; b:boolean; h,w:integer;

unit3.ImageShow(ss);

procedure ImageShow(s:string);

form2.Image1.Picture.LoadFromFile(s);//загружаем картинку из файла, чтобы в дальнейшем получить её размеры.

form2.Image1.Proportional:=true; //устанавливаем свойство Proportional компонента Image1 в true.

form2.Image1.Width:=w_rem; //устанавливаем исходные размеры

end //точка с запятой не ставится .

else

w:=form2.Image1.Picture.Width; //получаем размер картинки (ширину и высоту)

end; //конец оператора if

form2.Image1.Picture.LoadFromFile(s); //перезагружаем картинку, чтобы применить новые параметры.

Теперь рассмотрим связь модулей между собой.

Как мы отмечали ранее, модуль Unit1 обращается к модулю Unit2, вызывая его отображение: form2.Show;

Поэтому в модуле Unit1 указываем, что модуль Unit1 использует модуль Unit2:

uses Unit2;

Модуль Unit2 обращается к модулю Unit3 для использования расположенной в ней процедуры. Поэтому в модуле Unit2 указываем:

uses Unit3;

Далее, один модуль может видеть переменные и функции в другом модуле, только если они описаны в секции Interface. Если переменные описаны в секции implementation то они видны только внутри этого модуля.

В модуле Unit3 расположена процедура ImageShow(). Чтобы она была видна извне, её описание надо разместить в секции Interface модуля:

procedure ImageShow(s:string);

Теперь все модули, в которых есть ссылки в uses на Unit3, могут обращаться к этой процедуре.

С другой стороны, в процедуре ImageShow() есть обращение к переменным h_rem и w_rem. Эти же переменные первоначально используются в модуле Unit2. Поэтому помещаем описание этих переменных в секцию Interface этого модуля:

interface

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ExtCtrls, Menus,JPEG, Spin;

var h_rem,w_rem:integer;

Результаты работы программы.

Отображение картинок в реальных размерах.

delphi image

delphi image

В первом случае картинка полностью вписывается в размеры панели (её размеры меньше).

Во втором — появились полосы прокрутки, так как размер картинки больше размеров панели.

Отображение первой картинки не изменится (она и так вписывалась в размеры панели), а вот вторая картинка ужмётся, полностью одним из своих размеров вписавшись в панель.

Здравствуйте, IgoX, Вы писали:

IX>Привет всем
IX>Есть ScrollBox на котором динамически прорисовываются картинки TImage. Нужно сделать обрамление типа border на текущем имедже. Прорисовывал TShape на событие onMouseMove, но если выходишь с площади имиджа то его убрать можно только если повеситьтся на событие ScrollBox. А если включить поверх какое нибудь окно или быстро дернуть мышкой то событие на scrollbox не срабатывает. Как рационально сделать обрамление?

Не совсем понятно что же нужно сделать.
Но если нужно добиться эффекта появления рамки когда мышь над картинкой то перекрой методы:
CMMouseEnter
CMMouseLeave

Здравствуйте, Аноним, Вы писали:

А>Нужна рамка вокруг всего рисунка, когда мышь на рисунке. Я кладу Shape на scroll. И делаю его visible когда мышь на изображении. Если убираю мышь с картинки то делаю visible=false на ScrollBox, но если быстро дернуть мышь и она перескочит на др. картинку то событие не срабатывает, и получается что уже вокрг двух изображений рамка. Идея мне
кажется неправильная.

Именно что неправильная идея

Имхо наиболее простой вариант — сделать отрисовку рамки функцией контейнера, например, формы, а не рисунка. Тогда форма по своему MouseMove спокойно сможет показывать, и перемещать рамку — то есть заведомо не появится нескольких рамок, а также спокойно отработает всяческие потери фокуса.

Что касается OnExit — посмотри ответ о CM_MOUSELEAVE

S>Именно что неправильная идея
S>Имхо наиболее простой вариант — сделать отрисовку рамки функцией контейнера, например, формы, а не рисунка.

Не могу всецело согласиться
Напротив, неплохое решение — выполнить этакую частичную инкапсуляцию комплекса картинка-обрамление. То есть пусть этот комплекс сам себя отрисовывает так, как считает нужным. Иначе можно и до того дойти, что текущий элемент TListBox отрисовывать в обработчиках формы
В смысле дальнейшего развития идеи можно положить в потомок TImage ссылку на обрамление.

Привет всем
Есть ScrollBox на котором динамически прорисовываются картинки TImage. Нужно сделать обрамление типа border на текущем имедже. Прорисовывал TShape на событие onMouseMove, но если выходишь с площади имиджа то его убрать можно только если повеситьтся на событие ScrollBox. А если включить поверх какое нибудь окно или быстро дернуть мышкой то событие на scrollbox не срабатывает. Как рационально сделать обрамление?

Здравствуйте, IgoX, Вы писали:

IX>Есть ScrollBox на котором динамически прорисовываются картинки TImage. Нужно сделать обрамление типа border на текущем имедже. Прорисовывал TShape на событие onMouseMove,

А зачем "прорисовывать"?

Не совсем понятно что ты хочешь. Если рамка должна постоянно присутствовать на экране — то есть изображение — скроллироваться внутри рамки — я бы положил Bevel или Shape на того же родителя, что и скроллбокс, по тем же координатам и поверх последнего. Как минимум Bevel прозрачен, так что должно работать.

Если же нужна рамка вокруг всего рисунка — скроллирующаяся вместе с ним — то я положил бы тот же самый Bevel/Shape в скроллбокс.

Здравствуйте, Softwarer, Вы писали:

S>Здравствуйте, IgoX, Вы писали:

IX>>Есть ScrollBox на котором динамически прорисовываются картинки TImage. Нужно сделать обрамление типа border на текущем имедже. Прорисовывал TShape на событие onMouseMove,

S>А зачем "прорисовывать"?

S>Не совсем понятно что ты хочешь. Если рамка должна постоянно присутствовать на экране — то есть изображение — скроллироваться внутри рамки — я бы положил Bevel или Shape на того же родителя, что и скроллбокс, по тем же координатам и поверх последнего. Как минимум Bevel прозрачен, так что должно работать.

S>Если же нужна рамка вокруг всего рисунка — скроллирующаяся вместе с ним — то я положил бы тот же самый Bevel/Shape в скроллбокс.

Нужна рамка вокруг всего рисунка, когда мышь на рисунке. Я кладу Shape на scroll. И делаю его visible когда мышь на изображении. Если убираю мышь с картинки то делаю visible=false на ScrollBox, но если быстро дернуть мышь и она перескочит на др. картинку то событие не срабатывает, и получается что уже вокрг двух изображений рамка. Идея мне кажется неправильная. Если поменять активное окно, а потом вернуть исходное и мышь окажется на третьем изображении то будет уже три рамки. А мне нужно только активное. Нехватает события onExit на компоненте Image. Писать hook это слишком сильно для такой задачи.

S>Но если нужно добиться эффекта появления рамки когда мышь над картинкой то перекрой методы:
S>CMMouseEnter
S>CMMouseLeave
— у картинки.

Здравствуйте, Slicer [Mirkwood], Вы писали:

S>>Имхо наиболее простой вариант — сделать отрисовку рамки функцией контейнера, например, формы, а не рисунка.
SM>Не могу всецело согласиться
SM>Напротив, неплохое решение — выполнить этакую частичную инкапсуляцию комплекса картинка-обрамление.

Хм. Я сказал "наиболее простой", а не "наиболее правильный" Принципиальной кривизны я в этом не вижу, поскольку компонент "image c появляющейся рамкой" вряд ли нужен вне этого контейнера, а делать автору вопроса скорее всего будет действительно проще.

В этой статье описывается создание нестандартных окон.
Для этого нам понадобится Delphi (желательно Delphi 7 или RAD Studio).
Все графические объекты созданы в Photoshop 11.

Для начала уберём стандартный заголовок у окна:
Form1>Border Style>bsNone
и
Form1>Position>poScreenCenter

1.Делаем графический заголовок.
В виде заголовка у нас будет TImage.
Поставьте его на форму и присвойте ему имя Title (Image1>Name>Title).
Загрузите в него изображение (можно самое простое нарисовать в пеинте, но если хочется чего то более стильного делаем в фотошопе).
Установите в свойстве Align значение alTop.
И Height (Высоту) желательно 25, или как захотите только не слишком большую.
Ставим свойство Stretch: True
Теперь программируем заголовок:

Теперь окно можно (таскать) за наш заголовок.

2. Делаем рамку
Кидаем на форму три компонента TImage и присваиваем им имена:
Image1>Name>Left
Image2>Name>Right
Image3>Name>Bottom

Имагу Left ставим свойство Align>alLeft и Cursor: crSizeWE
Имагу Right ставим свойство Align>alRight и Cursor: crSizeWE
Имагу Bottom ставим свойство Align>alBottom и Cursor: crSizeNS
И у всех них свойство Stretch: True

Делаем ширину у имагов Left и Right 5,
а высоту у Bottom тоже 5.
Загружаем в них изображения.
Программируем:

Теперь дело остаётся за малым: кнопка закрыть, свернуть и цвет фона окна.
Поставьте на заголовок два компонента TImage и загрузите в них изображения. Меняем их названия:
Image1>Name>Close1
Image1>Name>Minimize
Программируем:

Фрейм (рамка, frame) представляет собой контейнер для других компонентов, который на этапе разработки создается так же, как форма, но, в отличие от нее, может быть размещен в другом контейнере, например, в форме или панели. Для работы с фреймами в Delphi служит компонент Frame.

Работа с фреймом состоит из двух этапов:

  1. Создание и конструирование фрейма.
  2. Размещение созданного фрейма в нужном месте формы или панели.


Создание фрейма выполняется командой File ? New ? Frame (Файл ? Новый ? Фрейм), в результате которой в состав проекта включается новый фрейм. Фрейм имеет много общего с формой. Так, для каждого фрейма создаются файлы формы и модуля, а вид фрейма на этапе разработки не отличается от вида формы. Конструирование фрейма, как и конструирование формы, заключается в размещении в нем нужных компонентов и в кодировании обработчиков событий. На скриншоте показан фрейм, в котором после его добавления к проекту размещены группа переключателей и кнопка.

Для размещения фрейма в форме следует выбрать в Палитре компонентов компонент Frame и поместить его в нужное место формы. При отпускании компонента появляется диалоговое окно Select frame to insert (Выберите фрейм для вставки), в котором выбирается имя фрейма. После нажатия кнопки ОК выбранный фрейм вставляется в указанное место. При этом несколько изменяется вид фрейма: у него пропадают рамка и заголовок, присущие форме. Однако размещенные в нем компоненты (т. е. содержимое фрейма) выглядят так же, как при разработке.

После размещения фрейма в форме в ее раздел uses автоматически включается ссылка на модуль фрейма, а в описание класса формы добавляются ссылки на обработчики событий фрейма и его компонентов.

В случае частого использования какого-либо фрейма его можно разместить в Палитре компонентов. Для этого после создания фрейма следует выделить его и выполнить команду Create Component Template (Создать шаблон компонента) меню Component (Компонент), что вызовет появление диалогового окна Component Template Information (Информация о шаблоне компонента).

Можно добавить фрейм и в Хранилище объектов, вызвав командой Add То Repository (Добавить к Хранилищу объектов) контекстного меню фрейма одноименное диалоговое окно.

Перед добавлением фрейма необходимо указать данные в следующих полях:

  • список Forms (имя добавляемого фрейма);
  • поле Title (название фрейма в Хранилище объектов);
  • поле Description (описание фрейма);
  • список Page (страница, на которой должен быть размещен фрейм);
  • поле Author (информация о разработчике фрейма).

Чтобы сменить значок фрейма, назначаемый по умолчанию, нажмите кнопку Browse (Просмотреть) и выберите в открывшемся диалоговом окне файл с расширением ico. После нажатия кнопки ОК фрейм добавляется в Хранилище объектов.

GDI расшифровывается как Graphics Device Interface, и представляет собой интерфейс, который Windows использует для рисования 2D графики. Также это самый медленный способ отображения графики из существующих, однако самый простой для понимания основ. Итак, для начала, поговорим об основных понятиях и терминах в GDI.

Начнём с того, что GDI обычно не используют для создания крутых графических эффектов, для этого есть DirectX, OpenGL, или любые графические библиотеки (такие как: DelphiX, FastLib, DIBUltra, Graphics32. ). Однако, для создание простых эффектов с минимальными усилиями GDI вполне сгодится.

С GDI тесно связана ещё одна аббревиатура - DC ("Device Context" - контекст устройства). Это то, на чём мы рисуем, и в Delphi контекст устройства представлен как TCanvas. Идея контекста устройства заключается в том, что это универсальное устройство вывода, поэтому можно использовать одинаковые функции как для экрана, так и для принтера.

Все графические функции в Delphi являются надстройками над стандартными GDI функциями Windows. Позже мы поговорим об этих функциях.

А теперь самое время приступить к рассмотрению того, как устроен GDI. Ниже, в таблице, представлены некоторые важные классы:

Однако, пора переходить от слов к делу, а именно, начать рисовать линии и фигуры.

Рисование линий

Сперва необходимо чётко уяснить, что координата (0,0) это верхний левый угол экрана. То есть значения по оси y увеличиваются вниз экрана. Соответственно, координата (0, 50) означает, что мы просто отступили на 50 пикселей от верха экрана.

Самое главное, что надо знать при рисовании линий и фигур, это различие между пером (Pen) и кистью (Brush). Всё очень просто: перо (Pen) используется при рисовании линий или рамок, а кисть (Brush) для заполнения фигуры.

Ниже приведены две функции, которые используются для рисования линий и обе принадлежат TCanvas:

Эффект перемещения точки начала рисования линии так же достигается при помощи установки своства PenPos в канвасе. например, "Canvas.PenPos.x := 20;", "Canvas.PenPos.y := 50", или "Canvas.PenPos := Point(20,50);".

По умолчанию, точка начала рисования установлена в (0,0), то есть, если сразу вызвать "Canvas.LineTo(100,100);" то будет нарисована линия из точки (0,0) в точку (100, 100). Точка начала рисования автоматически переместится в (100, 100), то есть, если выполнить команду "Canvas.LineTo(200, 100);", то следующая линия будет нарисована из точки (100, 100) в (200, 100). Поэтому, если мы хотим рисовать линии несоединённые друг с другом, то придётся воспользоваться методом MoveTo.

Линия, нарисованная при помощи LineTo использует текущее перо канваса (типа TPen). Основные свойства пера, это ширина - "Canvas.Pen.Width := 4;" (при помощи которого можно задавать различную ширину линий), и цвет "Canvas.Pen.Color := clLime;".

Взглянем на простой пример беспорядочного рисования разноцветных линий:

Процедура DrawLines вызывается из обработчика кнопки OnClick. Количество линий задаётся в константе NUM_LINES. Между прочим, функция RGB, составляет цвет каждой линии из трёх основных составляющих: красного, зелёного и синего (значения от 0 до 255) и возвращает нам цвет в виде TColor. О цветах поговорим немного позже, а вот так выглядит нарисованный пейзаж:

Теперь, когда линии нарисованы, попробуйте немножко подвигать форму. Если форму переместить за края экрана, то Вы увидите, что линии частично стёрлись. Это не глюк, и решается эта проблема очень просто. Но не сейчас ;-). Сперва посмотрим, как рисовать различные фигуры.

Рисование фигур

Для рисования фигур, в TCanvas предусмотрены следующие функции:

Ещё есть очень нужная функция TextOut, которая позволяет рисовать текст, используя шрифт, заданный в канвасе:

Кстати, функция позволяет рисовать текст, не заполняя его фон. Если Вам необходимо изменить шрифт, используемый в TextOut, то необходимо изменить свойство Font канваса (это свойство имеет тип TFont) - например "Canvas.Font.Name := 'Verdana';", "Canvas.Font.Size := 24;" или "Canvas.Font.Color := clRed;".

Вкратце хотелось бы обратить Ваше внимание на довольно полезный класс TRect, который умеет хранить в себе значения лево, право, верха и низа (кстати, в Windows API это RECT). То ест, достаточно указать левую и верхнюю координату и ширину и высоту области, а TRect автоматически подставит в виде (лево, верх, лево + ширина, верх + высота). Ещё есть другая функция Rect(), которая делает тоже самое, но координаты в ней задаются напрямую как лево, право, верх и низ. Ну и по желанию, можно использовать API функцию SetRect.

Ниже представлен пример, который рисует случайным образом различные фигуры:

Как Вы уже успели заметить, некоторые фигурки имеют цвет рамки, отличающийся от того цвета, которым заполнена фигура. Это как раз тот момент, о котором я упоминал выше. Кистью мы заполняем объекты, а пером обрамляем. Если цвет кисти (brush) меняется случайным образом, то цвет пера(pen) остаётся постоянным. Из-за этого и получается такая картина.

Перерисовка окна

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

Рисование, это то, что мы делали выше. То есть, рисовали любые линии и графические фигуры. Однако, рисунок сохранялся до тех пор, пока окно(форма) не было обновлено.

Для примера, поместите следующий код в проект:

Если поместить на форму кнопку и вызывать DrawSomeText из обработчика кнопки OnClick, то проблема с исчезновением текста при перемещении формы останется. ОДНАКО, если вызвать DrawSomeText из обработчика формы OnPaint, то текст останется на своём месте окончательно.

Дескрипторы, или как пользоваться аналогичными API функциями

Итак, мы научились рисовать линии, различные фигуры, научились делать так, чтобы наше творение не стиралось при перемещении формы, и проделали мы всё это при помощи стандартных функций VCL (таких как Canvas.TextOut и т.д.). Однако, что делать, если Вы не хотите пользоваться графическими функциями VCL, которые всего навсего являются надстройками над аналогичными функциями из Windows API? Пожалуйста! Никто нам не запрещает пользоваться API функциями напрямую! Но постойте-ка, все они требуют какого-то HDC! Что такое HDC?

Почти всё в Windows использует "Дескриптор" (Handle). Дескриптор, это способ идентификации Вашего объекта в системе. У каждого окна есть свой дескриптор, у каждой кнопки тоже есть свой дескриптор и т.д. Именно поэтому все наши объекты имеют дескриптор в качестве свойства - например, "MyForm.Canvas.Handle".

Тип HDC это Дескриптор(Handle) Контекста Устройства (Device Context). Я уже говорил в самом начале, что TCanvas включает в себя большинство функций DC. Поэтому, мы спокойно можем подставлять свойство канваса Handle везде, где нам это потребуется.

Ради интереса можно взглянуть на таблицу, в которой представлены примеры вызовов некоторых функций из VCL и их аналогов из Windows API.

Так же можно использовать разные дескрипторы, чтобы рисовать в разных местах. Например, можно использовать "SomeBmp.Canvas.Handle" для рисования на картинке (битмапе), либо "Form1.Canvas.Handle", чтобы рисовать на форме.

В API версии функции TextOut необходимо передавать строку завершённую нулём. Это значит, что вместо того, чтобы передать строку в функцию напрямую, необходимо передать её как PChar. Так же не забывайте передавать в функцию длину строки. Для этого можно воспользоваться функцией Length.

Ну что, Вам уже захотелось поместить на форму какую-нибудь красивую картинку ?

Что такое Битмапы (Bitmaps)?

Рисование линий, это, конечно, хорошо, но рано или поздно Вам понадобится нарисовать более реалистичную картнику в своём приложении. Поэтому совершенно необходимо научиться работать с растровыми изображениями, или как их называют в среде программистов - битмапами.

Битмап, это графический объект, который содержит заголовок, необходимую информацию о картинке (такую как высота, ширина, цвета и т.д.) и, собственно, само изображение (большой массив, содержащий цвет каждой точки). В Delphi для этой цели уже предусмотрен класс TBitmap.

Битмапы можно рисовать не только на форме, но и по всему экрану. Может это и может показаться немного странным, но иногда это бывает полезно, особенно при создании скринсейвера. Однако, сначала нам необходимо разобраться с тем, как работать с битмапами. Вот небольшой пример:

Эта функция пытается загрузить и показать картинку, (с именем Filename, например 'myBitmap.bmp') начиная с точки (x,y).

Сразу скажу, что эта функция довольно неэффективна. Она создаёт и уничтожает битмап каждый раз когда вызывается, а так же каждый раз проверяет существование файла. Лучше объявлять объект TBitmap как часть формы, создавать и загружать картинку в FormCreate, а освобождать её в FormDestroy.

Функции рисования в GDI

TCanvas имеет несколько полезных функций, которые работают с типом TGraphic. Тип TGraphic является базовым классом для графических объектов в Delphi, таких как: битмапы (TBitmap), иконки (TIcon), метафайлы (TMetafile) и JPEG-и (TJPEGImage). Все они используют одни и те же функции, которые приведены в таблице:

Все эти функции являются методами TCanvas.

TCanvas.Draw является обёрткой для API функции BitBlt:

Описанный выше способ позволяет рисовать битмап в run-time. Конечно, проще поместить на форму TImage и установить в ней картинку. Изображение будет постоянно оставаться на том месте, где Вы его поместили, но это скучно ;-). Куда интереснее сделать при помощи битмапов анимацию.

С другой стороны, поняв принципы работы с битмапами, Вам будет легче перейти к другим графическим библиотекам (например DirectX).

Читайте также: