Компонента позволяет манипулировать подсказками атрибутов формы и табличного документа во время выполнения программы. Текст подсказки может быть разным как для каждого атрибута формы, так и для каждой строки в табличной части справочника, документа, журнала документов, а также каждой колонки или ячейки.
Текст подсказки формируется подобно HTML странице и может включать форматирование с использованием различных шрифтов, цветов, графики.
Текст подсказки и картинки могут быть загружены из отдельных файлов, файлов, содержащих ресурсы, из библиотеки картинок конфигурации.
Окно подсказки может иметь округлую форму, хвост, тень, может быть полупрозрачным. Окно может появляться постепено и так же исчезать.
В данной книге речь пойдет о способах создания анимации в Macromedia Flash 4. Для начала следует понять: в чем секрет такой компактности роликов, созданных в Macromedia Flash? Основная причина состоит в том, что технология Flash реализует анимацию векторных объектов. На практике это означает, что для создания элементарного ролика нам нужно один раз создать анимируемый объект и в дальнейшем манипулировать только его атрибутами и формой. Технология так же позволяет использовать и растровые объекты.
В основе анимации Macromedia Flash лежит принцип преобразований по ключевым кадрам (Keyframe). Т.е. достаточно задать во времени только начальный и конечный кадр элементарного преобразования, а промежуточные кадры будут выстроены программой без вашего вмешательства. К элементарным преобразованиям относятся: изменение координат, размеров, масштаба, угла поворота, формы, цветовых атрибутов контура и заливки объекта.
Преобразования реализуются двумя основными методами:
Движение (Motion-tweening) - метод предназначен для изменения атрибутов объекта.
Превращение (Shape-tweening) - метод предназначен для преобразования формы одного объекта в другой. Для создания аналогичных эффектов вызывает увеличение размера файла ролика по сравнению с Motion-tweening.
Крайним случаем является покадровая анимация, неизбежная при создании мультфильмов. Такой подход увеличивает размер файла, т.к. анимация происходит за счет покадровой смены объектов, описание которых требует существенно большего объема, нежели описание изменения атрибутов разумно ограниченного числа объектов.
Прикладное программирование для Web начиналось с обработки запросов пользователя и динамической генерации страниц на стороне сервера. Эта же тенденция получила развитие и в языках программирования вставок в HTML-документы. Затем появились языки программирования элементов HTML-документов на стороне клиента. И те, и другие привязаны к модели данных HTML. Сегодня, когда Web мигрирует к стеку спецификаций XML, разработчики средств программирования должны это учесть и правильно отреагировать, позволив, например, манипулировать элементами XML-разметки. Стандарт, призванный обозначить и решить эту задачу, получил название Document Object Model - DOM.
Потоки всегда создаются в контексте какого-либо процесса, и вся их жизнь проходит только в его границах. На практике это означает, что потоки исполняют код и манипулируют данными в адресном пространстве процесса. Если два или более потока выполняются внутри одного процесса, они делят одно адресное пространство.
Любой поток (thread) состоит из двух компонентов:
объекта ядра, через который ОС управляет потоком. Там же хранится статистическая информация о потоке.
Стека потока, который содержит параметры всех функций и локальные переменные, необходимые потоку для выполнения кода.
Потоки могут выполнять один и тот же код, манипулировать одними и теми же данными, а также совместно использовать описатели объектов ядра, поскольку таблица описателей создается не в отдельных потоках, а в процессах.
Потоки используют намного меньше ресурсов системы, чем процессы, поэтому все задачи, требующие параллельного выполнения нескольких подзадач, стоит решать по возможности с помощью потоков, не прибегая к созданию нескольких процессов.
Обычная структура многопоточного приложения рассчитана на одновременное исполнение нескольких подзадач. Однако стоит помнить, что, создавая многопоточное приложение, нам придется заботиться о сохранности и ликвидности, общих для всех потоков, данных.
Создание потока.
Первичный поток, который присутствует в программе, начинает свое выполнение с главной функции потока типа WinMain.
Для создания вторичного потока необходимо создать и для него входную функцию, которая выглядит примерно так:
Имя у функции вторичного потока, в отличии от первичного, может быть любым однако, при наличии нескольких разных потоков, назвать функции необходимо по-разному, иначе система создаст разные реализации одной и той же функции.
Когда поток закончит свое исполнение, он вернет управление системе, память, отведенная под его стек, будет освобождена, а счетчик пользователей его объекта ядра "поток" уменьшится на 1. Когда счетчик обнулится, этот объект ядра будет разрушен.
Для создания своего потока необходимо использовать функцию CreateThread:
При каждом вызове этой функции система создает объект ядра (поток). Это не сам поток, а компактная структура данных, которая используется операционной системой для управления потоком и хранит статистическую информацию о потоке.
Система выделяет память под стек потока из адресного пространства процесса. Новый поток выполняется в контексте того же процесса, что и родительский поток. Поэтому он получает доступ ко всем описателям объектов ядра, всей памяти и стекам всех потоков в процессе. За счет этого потоки в рамках одного процесса могут легко взаимодействовать друг с другом.
CreateThread - это Windows-функция, создающая поток. Если вы пишете код на С/С++ не вызывайте ее. Вместо нее Вы должны использовать _beginthreadex из библиотеки Visual C++. Почему это так важно в наших следующих выпусках.
Параметры функции CreateThread.
LpThreadAttributes - является указателем на структуру LPSECURITY_ATTRIBUTES. Для присвоения атрибутов защиты по умолчанию, передавайте в этом параметре NULL.
DwStackSize - параметр определяет размер стека, выделяемый для потока из общего адресного пространства процесса. При передаче 0 - размер устанавливается в значение по умолчанию.
LpStartAddress - указатель на адрес входной функции потока.
LpParameter - параметр, который будет передан внутрь функции потока.
DwCreationFlags - принимает одно из двух значений: 0 - исполнение начинается немедленно, или CREATE_SUSPENDED - исполнение приостанавливается до последующих указаний.
LpThreadId - Адрес переменной типа DWORD в который функция возвращает идентификатор, приписанный системой новому потоку.
Завершение потока
Поток можно завершит четырьмя способами:
функция потока возвращает управление (рекомендуемо);
поток самоуничтожается вызовом функции ExitThread;
другой поток процесса вызывает функцию TerminateThread;
завершается процесс, содержащий данный поток.
Все способы , за исключением рекомендуемого, являются нежелательными и должны использоваться только в форс-мажорных обстоятельствах.
Функция потока, возвращая управление, гарантирует корректную очистку всех ресурсов, принадлежащих данному потоку. При этом:
любые С++ объекты, созданные данным потоком, уничтожаются соответствующими деструкторами;
система корректно освобождает память, которую занимал стек потока;
система устанавливает код завершения данного потока. Его функция и возвращает;
счетчик пользователей данного объекта ядра (поток) уменьшается на 1.
При желании немедленно завершить поток изнутри используют функцию ExitThread(DWORD dwExitCode).
При этом освобождаются все ресурсы ОС, выделенные данному потоку, но С С++ ресурсы (например, объекты классов С++) не очищаются. Именно поэтому не рекомендовано завершать поток, используя эту функцию.
Если же вы ее использовали, то кодом возврата потока будет тот параметр, который вы передадите в данную функцию.
Как и для CreateThread для библиотеки Visual C++ существует ее аналог _endthreadex, который и стоит использовать. Об причинах в следующем выпуске.
Если появилась необходимость уничтожить поток снаружи, то это моет сделать функция TeminateThread.
Эта функция уменьшит счетчик пользователей объекта ядра (поток) на 1, однако при этом не разрушит и не очистит стек потока. Стек будет существовать, пока не завершится процесс, которому принадлежит поток. При задачах, постоянно создающих и уничтожающих потоки, это приводит к потере памяти внутри процесса.
При завершении процесса происходит следующее.
Завершение потока происходит принудительно. Деструкторы объектов не вызываются, и т.д. и т.д.
При завершении потока по такой причине, связанный с ним объект ядра (поток) не освобождается до тех пор, пока не будут закрыты все внешние ссылки на этот объект.
Регионы нужны не только для того, чтобы резать дырки в формах. Иногда они могут оказаться довольно полезным инструментом именно в своём "родном" качестве, т.е. для отрисовки на экране достаточно сложных геометрических фигур. Например, для вывода карт, представляющих собой совокупность ломанных линий, построенных по массивам точек. Создать такую линию нам уже не составит труда, пора разобраться, как её показать юзеру.
Из функций отрисовки две первые нам уже смутно знакомы: они делают тоже, что делает параметр FillMode (ALTERNATE/WINDING) для функций CreatePolygonRgn и CreatePolyPolygonRgn. GetPolyFillMode получает заданный для указанного контекста режим заливки, а SetPolyFillMode устанавливает его. Просто на этот раз речь идёт не о создании региона, а всего лишь о его отрисовке. Установленное значение будет иметь смысл для всех функций, заливающих регион, т.е. PaintRgn и FillRgn, при этом сам регион останется таким, каким он и был создан, а вот раскрашен будет по разному, в том случае, если он состоит из нескольких пересекающихся регионов. Для простых регионов типа прямоугольника или элипса установка данного значения ничего не меняет.
Итак. Давайте срочно что-нить создадим и нарисуем. Можно, конечно, сделать это в одной функции, например в OnCreate, но тогда изображение будет весьма недолговечным - до первой перерисовки формы. Поэтому поступим иначе: объявим private property fRgn, в OnCreate его инициализируем, в OnPaint будем его отображать, а в OnDestroy - уничтожим. Код методов представлен ниже:
Следует помнить, что Функции отрисовки регионов всегда работают с цветом,
указанным в Canvas.Brush.Color. Даже рисуя бордюр (frame) использоваться будет не цвет Canvas.Pen, что, в общем-то, представляется более логичным, а цвет Canvas.Brush.
Ничего такой получился кружочек. Погребального вида. Давайте сделаем его более жизнерадостным, и заодно разберёмся, как работает FrameRgn:
У меня получилась такая вот картинка:
Насколько я могу судить, функции FillRgn и PaintRgn отличаются друг от друга только тем, что первая позволяет указать дескриптор кисти, не связанной с текущим canvas'ом. Сомнительная фича с точки зрения дельфей, т.к. манипулировать с текущим цветом кисти канваса всяко легче, чем создавать отдельный экземпляр класса TBrush. Вот, собственно, и всё об отрисовке. Примечательно то, что для того, чтобы нарисовать регион нам не нужно знать, что он из себя представляет. Мы просто передаём дескриптор одной и той же процедуре, а она отобразит на экране круг, овал, треугольник, звезду Давида - всё, что угодно.
Функции, представленные в разделе прочее ничего особенно интересного из себя не представляют, и, в общем-то, интуитивно понятны. поэтому рассотрим лишь некоторые из них.