Опубликован: 08.08.2015 | Доступ: свободный | Студентов: 281 / 38 | Длительность: 09:22:00
Лекция 2:

Меню. События мыши

< Лекция 1 || Лекция 2 || Лекция 3 >
Аннотация: Рассматриваются основные виды меню и события мыши. Создается панель инструментов, всплывающее меню и меню окна. Для создания изображений используется мышь. Cтроится выпуклая оболочка множества точек.

Создание панели инструментов

Подготовим к использованию новую форму в проекте drawing (см. п. 1.2 "Основные элементы графического интерфейса пользователя" ).

Для этого следует выполнить следующие действия:

  • создать новую форму paintForm и удалить из нее все кнопки;
  • создать новый подпункт Paint ("Рисование") пункта меню Тест;
  • добавить обработчик события выбора команды меню Тест -> Рисование.

Обработчик выбора команды меню определяется так же, как и ранее:

predicates
    onTestPaint : window::menuItemListener.
clauses
    onTestPaint(_Source, _MenuTag):-
        _ = paintForm::display(This).

Редактор панели инструментов

Создадим панель инструментов окна paintForm.

Выделим папку paintForm дерева проекта и выберем пункт New In New Package (контекстного) меню. Выделение узла дерева проекта (папки) используется для того, чтобы новый узел оказался внутри этой папки.

В окне Create Project Item выделим элемент Toolbar, в поле Name напишем имя paintToolbar, затем нажмем кнопку Create. В результате откроется окно атрибутов панели инструментов Toolbar Attributes (рис. 2.1 рис. 2.1). В поле Style выберем элемент Left (слева). Затем нажмем кнопку OK.

Окно атрибутов панели инструментов

Рис. 2.1. Окно атрибутов панели инструментов

После этого откроется редактор панели инструментов (рис. 2.2 рис. 2.2).

Редактор панели инструментов

Рис. 2.2. Редактор панели инструментов

Закроем редактор.

Подготовка изображений

Создадим изображения (bitmap), которые будут помещаться на кнопки панели инструментов. Выделим папку paintToolbar дерева проекта и выберем команду всплывающего меню New In Existing Package. В окне Create Project Item выделим элемент Bitmap, в поле Name напишем color. После нажатия кнопки Create появится окно установки атрибутов изображения (рис. 2.3 рис. 2.3).

Окно атрибутов изображения

Рис. 2.3. Окно атрибутов изображения

Оставим параметры, установленные по умолчанию, и закроем это окно. Откроется графический редактор, показанный на рис. 2.4 рис. 2.4.

Графический редактор изображения

Рис. 2.4. Графический редактор изображения

Далее следует нарисовать изображение, затем закрыть окно, сохранив изменения.

Аналогично создаются растровые изображения (bitmap) point, marker, eraser, clear и convexhull. В готовом виде они показаны на рис. 2.5 рис. 2.5.

Панель инструментов окна

Рис. 2.5. Панель инструментов окна

Размещение кнопок на панели инструментов

Поместим кнопки на панель инструментов. Откроем редактор панели инструментов paintToolbar.tb (см. рис. 2.2 рис. 2.2). В окне Controls нажмем на кнопку с надписью OK (рис. 2.6 рис. 2.6).

Окно Controls

Рис. 2.6. Окно Controls

В результате откроется диалоговое окно атрибутов кнопки Button Attributes (рис. 2.7 рис. 2.7).

Окно атрибутов кнопки

Рис. 2.7. Окно атрибутов кнопки

Заполним поля следующим образом:

  • Constant: id_color;
  • Status Text: цвет;выберите цвет;
  • Bitmap Name: color.

В поле Constant указывается идентификатор команды меню, в поле Status Text — тексты подсказок. При наведении курсора на кнопку color появится подсказка (Tooltip) "цвет". Одновременно в строке состояния (Status Line) отобразится текст "выберите цвет".

Аналогично создаются кнопки point (Constant: id_point), marker (id_marker), eraser (id_eraser), clear (id_clear) и convexhull (id_convexhull). Перед последней кнопкой добавлен разделитель (Separator) (рис. 2.8 рис. 2.8).

Создание кнопок в редакторе панели инструментов

Рис. 2.8. Создание кнопок в редакторе панели инструментов

Далее следует закрыть редактор панели инструментов.

Размещение панели инструментов в окне

Теперь необходимо разместить панель инструментов в окне. Для этого следует открыть редактор формы paintForm и для свойства Assigned Toolbar таблицы свойств окна Properties выбрать элемент paintToolbar. Далее нужно закрыть редактор формы, сохранив изменения. Результат показан выше на рис. 2.5 рис. 2.5.

События мыши

Идентификаторы всех ресурсов автоматически записываются в интерфейс resourceIdentifiers (файл resourceIdentifiers.i открывается из дерева проекта). В раздел open имплементации класса paintForm следует добавить имя интерфейса resourceIdentifiers:

open core, vpiDomains, resourceIdentifiers

Весь приведенный ниже код следует поместить в имплементацию класса paintForm.

Инструментами создания изображения в нашем приложении являются "карандаш", "маркер" и "ластик". "Карандаш" ставит точки размером в один пиксель, маркер — крупные "точки" (квадраты) размером 10х10 пикселей, ластик имеет размер 20х20 пикселей. Координаты точек сохраняются в базе данных.

Построение изображения

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

domains
    dotType = dot; marker; eraser.
constants
    dotsize = 5.			      % половина стороны крупной точки
    erasersize = 10. 		    % половина стороны ластика
    
facts - dbpoints					                % база данных точек
    point: (pnt, dotType, color).
    
facts
    switch : dotType := dot.			        % метка команды меню
    convexhull : pnt* := [].			        % вершины вып. обол.
    bgcolor : color := color_White.		    % цвет фона
    currentColor : color := color_Blue.		% текущий цвет
    
    isMouseDown : boolean := false. 	    % нажатие кнопки мыши 
Листинг 2.1. Объявление базы данных точек и основных параметров

Добавим с помощью редактора формы paintForm обработчики событий SizeListener, PaintResponder и EraseBackgroundResponder. Предикаты onSize и onEraseBackground определяются так же, как и ранее.

clauses
    onSize(_Source):-
        invalidate().
        
clauses
     onEraseBackground(_Source, _GDI) = noEraseBackground.
Листинг 2.2. Определение предикатов onSize и onEraseBackground

Ниже приведено определение предиката onPaint.

clauses
    onPaint(_Source, Rectangle, GDI):-
        true = isMouseDown,
        !,
        drawPoint(GDI, switch, Rectangle).
    onPaint(_Source, _Rectangle, GDI):-
        GDI:clear(bgcolor),
        point(Pnt, Type, Color),
            drawPoint(GDI, Type, Pnt, Color),
        fail.
    onPaint(_Source, _Rectangle, GDI):-
        convexhull <> [],
        !,
        GDI:setPen(pen(1, ps_Solid, color_Black)),
        GDI:setBrush(brush(pat_Hollow, color_Black)), 
        GDI:drawPolygon(convexhull).
    onPaint(_Source, _Rectangle, _GDI).
Листинг 2.3. Определение предиката onPaint

Все рисование выполняется предикатом onPaint. Первое правило используется непосредственно в процессе рисования при нажатой левой кнопке мыши: обновляется только квадрат размером с "точку" или "ластик". Второе правило строит все изображение. Третье правило строит изображение выпуклой оболочки по списку вершин, который хранит факт-переменная convexhull: рисуется многоугольник с тонкой черной границей и прозрачной заливкой.

Ниже приведено определение предикатов drawPoint/3 и drawPoint/4, а также вспомогательных предикатов.

predicates
    drawPoint: (windowGDI, dotType, rct).
    drawPoint: (windowGDI, dotType, pnt, color).
clauses
    drawPoint(GDI, dot, rct(X, Y, _, _)):- !,
        GDI:drawPixel(pnt(X, Y), currentColor).
    drawPoint(GDI, Type, Rectangle):- !,
        drawRect(GDI, Rectangle, color(Type)).
        
    drawPoint(GDI, dot, Pnt, Color):- !,
        GDI:drawPixel(Pnt, Color).
    drawPoint(GDI, Type, Pnt, Color):-
        drawRect(GDI, rectangle(Pnt, Type), Color).
        
predicates
    drawRect: (windowGDI, rct, color).
clauses
    drawRect(GDI, Rectangle, Color):-
        GDI:setPen(pen(1, ps_Solid, Color)),
        GDI:setBrush(brush(pat_Solid, Color)),
        GDI:drawRect(Rectangle).
        
predicates
    rectangle: (pnt, dotType) -> rct.
    size: (dotType) -> integer.
    color: (dotType) -> integer.
clauses
    rectangle(pnt(X, Y), dot) = rct(X, Y, X + 1, Y + 1):- !.
    rectangle(pnt(X, Y), Type) = rct(X - S, Y - S, X + S, Y + S):-
        S = size(Type).
        
    size(eraser) = erasersize:- !.
    size(_) = dotsize.
    
    color(eraser) = bgcolor:- !.
    color(_) = currentColor.
Листинг 2.4. Предикаты рисования точек

Предикат drawPixel/2 закрашивает один пиксель.

Событие выбора команды меню

Как событие выбора пункта меню, так и событие нажатия кнопки на панели инструментов приводит к вызову обработчика событий MenuItemListener. Добавим его в редакторе формы paintForm. Каждое правило в его определении соответствует нажатию одной из кнопок панели инструментов.

predicates
    onMenuItem : window::menuItemListener.
clauses
    onMenuItem(_Source, id_color):- 		% выбор цвета
        NewColor = vpiCommonDialogs::getColor(currentColor),
        !,
        currentColor := NewColor.
    onMenuItem(_Source, id_clear):- !, 	% стирается рисунок
        retractFactDb(dbpoints),
        convexhull := [],
        invalidate().
    onMenuItem(_Source, id_point):- !,	% запоминается 
        switch := dot.				% команда меню
    onMenuItem(_Source, id_marker):- !,
        switch := marker.
    onMenuItem(_Source, id_eraser):- !,
        switch := eraser.
    onMenuItem(_Source, id_convexhull):- !,	 % строится
        convexhull := createConvexHull(),	% выпуклая оболочка
        invalidate().
    onMenuItem(_Source, _MenuTAG).
Листинг 2.5. Определение предиката onMenuItem

После нажатия кнопки выбора цвета открывается диалоговое окно "Цвет" (рис. 2.9 рис. 2.9). Факт-переменная currentColor хранит последнее выбранное значение цвета.

Окно выбора цвета

Рис. 2.9. Окно выбора цвета

После нажатия кнопки "Очистить" панели инструментов поле окна очищается, при этом удаляются все факты из базы данных точек. В результате нажатия кнопки "Выпуклая оболочка" строится выпуклая оболочка множества крупных "точек" (см. ниже). Остальные кнопки используются для рисования или удаления "точек".

Обработка событий мыши

В редакторе окна paintForm добавим обработчики событий мыши MouseDownListener, MouseMoveListener и MouseUpListener.

После нажатия левой кнопки мыши факт-переменная isMouseDown принимает значение true. Далее запускается процедура обработки координат точки касания курсора, которая зависит от того, какая кнопка на панели инструментов была нажата последней (см. предикат drawing/2).

predicates
    onMouseDown : window::mouseDownListener.
clauses
    onMouseDown(_Source, Point, _ShiftControlAlt, _Button):-
        isMouseDown := true,
        convexhull := [],
        drawing(switch, Point).
Листинг 2.6. Определение предиката onMouseDown

Во время перемещении указателя мыши при нажатой кнопке мыши выполняется та же процедура обработки координат точки касания указателя.

predicates
    onMouseMove : window::mouseMoveListener.
clauses
    onMouseMove(_Source, Point, _ShiftControlAlt, _Button):-
        true = isMouseDown,
        !,
        drawing(switch, Point).
    onMouseMove(_Source, _Point, _ShiftControlAlt, _Button).
Листинг 2.7. Определение предиката onMouseMove

После отпускания кнопки мыши факт-переменная isMouseDown принимает значение false.

predicates
    onMouseUp : window::mouseUpListener.
clauses
    onMouseUp(_Source, _Point, _ShiftControlAlt, _Button):-
        isMouseDown := false,
        invalidate().
Листинг 2.8. Определние предиката onMouseUp

Определение предиката drawing/2 приведено ниже.

predicates
    drawing: (dotType, pnt Point).
clauses
    drawing(Type, Point):-
        Rectangle = rectangle(Point, Type),
        erasePoint(Type, Rectangle),
        addPoint(Type, Point),
        invalidate(Rectangle).
        
predicates
    erasePoint: (dotType, rct).
    addPoint: (dotType, pnt).
clauses
    erasePoint(eraser, Rectangle):-
        point(Dot, Type, _),
            pntInsideEraser(Rectangle, Dot, Type),
            retractAll(point(Dot, _, _)),
        fail.
    erasePoint(_, _).
    addPoint(eraser, _):- !.
    addPoint(Type, Point):-
        retractAll(point(Point, Type, _)),
        assert(point(Point, Type, currentColor)).
        
predicates
    pntInsideEraser: (rct Rectangle, pnt Dot, dotType) determ.
clauses
    pntInsideEraser(Rect, Dot, dot):- !,
        vpi::rectPntInside(Rect, Dot).
    pntInsideEraser(Rect, Dot, Type):-
        rct(A, B, C, D) = vpi::rectIntersect(rectangle(Dot, Type), Rect),
        (C - A) * (D - B) > 2 * dotsize^2.
Листинг 2.9. Рисование и удаление точек

Предикат invalidate/1 помечает как недействительную прямоугольную область, в данном случае, размером с "точку" или "ластик", что приводит к ее перерисовке. Предикат rectPntInside/2 истинен, если точка находится внутри прямоугольника. Предикат rectIntersect/2 находит пересечение двух прямоугольников.

Маленькая "точка" удаляется из базы данных, если она попадает под ластик. Удаление крупной "точки" производится в том случае, если ластик покрывает ее более чем наполовину.

Упражнение. Создайте кнопку для выбора цвета фона рисунка и добавьте соответствующее правило для предиката onMenuItem.

Остается определить процедуру построения выпуклой оболочки.

Выпуклая оболочка

Подмножество M точек плоскости называется выпуклым, если для произвольных двух точек из M отрезок, который соединяет эти точки, целиком содержится в M. Выпуклой оболочкой множества X называется наименьшее выпуклое множество, содержащее множество X.

В нашем случае множество крупных точек X — это конечное подмножество точек плоскости, поэтому его выпуклой оболочкой является многоугольник.

Для построения границы выпуклой оболочки используем алгоритм Грэхема1Кормен Т.Х., Лейзерсон Ч.И., Ривест Р.Л., Штайн К. Алгоритмы. Построение и анализ. – Изд. 2-e. – М.: Издательский дом "Вильямс", 2007. – 1296 с. – Глава 33 Сначала находится самая левая точка P множества X. Затем на множестве остальных точек вводится следующее отношение порядка. Если точки Q и R не лежат на одном и том же луче, выходящем из точки P, то полагается $Q\leq R$, если система векторов $\overline{PQ}$, $\overline{PR}$ имеет положительную ориентацию. Это означает, что они ориентированы так же, как и орты координатных осей, так что определитель матрицы, составленной из координат этих векторов, положителен. Для точек P(x, y), Q(a, b) и R(c, d) этот определитель находится следующим образом:

\begin{vmatrix}
a-x \quad c-x\\
b-y \quad d-y\\
\end{vmatrix}=(a-x)(d-y)-(c-x)(b-y).

Если точки P, Q и R лежат на одной прямой, то полагается $Q\leq R$, если $|PQ|\leq |PR|$.

Множество точек, отличных от P, сортируется в описанном выше порядке. Далее из множества точек, лежащих на одном луче, выходящем из точки P, удаляются все точки, кроме одной, наиболее удаленной от точки P (рис. 2.10 рис. 2.10).

Порядок на множестве точек:

Рис. 2.10. Порядок на множестве точек:
$Q\leq R\leq U\leq T$

На следующем этапе алгоритма выполняется обход оставшегося множества точек, начиная с точки P. Используются список точек и стек, в который изначально помещаются точка P и затем первая из остальных точек. Напомним, что к этому моменту множество точек является упорядоченным, при этом из точек, лежащих на одном луче, оставлена одна, наиболее удаленная от P точка. Далее, пока не закончатся точки из списка, из него берется очередная точка K, а из стека последовательно берутся (но не удаляются) точка M и точка N. Если система векторов $\overline{MN}$, $\overline{MR}$ ориентирована положительно, то точка M удаляется из стека, и алгоритм продолжает работу для старого списка точек. В противном случае точка K перекладывается в стек, и алгоритм продолжает работу для оставшегося списка точек. Когда список окажется пустым, стек будет содержать множество вершин выпуклой оболочки.

Например, рассмотрим множество точек, изображенное на рис. 2.10 рис. 2.10.

После сортировки из него удаляется точка U, лежащая на одном луче с точкой T и расположенная ближе к P. В стек помещаются точки P и Q, так что верхней оказывается точка Q. Из списка точек берется точка S (рис. 2.11 (a) рис. 2.11). Ориентация векторов $\overline{QP}$, $\overline{QS}$ отрицательна, поэтому точка S помещается в стек. Из списка точек берется точка R (рис. 2.11 (b) рис. 2.11). Векторы $\overline{SQ}$, $\overline{SR}$ ориентированы положительно, поэтому точка S удаляется из стека. Ориентация системы векторов $\overline{QP}$, $\overline{QR}$ отрицательна (рис. 2.11 (c) рис. 2.11), поэтому точка R добавляется в стек, и так далее.

Обход Грэхема

Рис. 2.11. Обход Грэхема

Выпуклую оболочку множества крупных "точек" строит предикат createConvexHull.

predicates
    createConvexHull: () -> pnt*.
clauses
    createConvexHull() = graham::createConvexHull(PntList):-
        PntList = [Pnt || point(Pnt, marker, _)].
Листинг 2.10. Реализация алгоритма Грэхема

Создадим класс graham (не порождающий объекты). В его декларации (файл graham.cl) объявим предикат createConvexHull.

predicates
    createConvexHull: (vpiDomains::pnt*) -> vpiDomains::pnt*.
Листинг 2.11. Объявление предиката построения выпуклой оболочки

Ниже приведена имплементация класса graham.

    open core, vpiDomains

clauses
    createConvexHull(PntList) = ConvexHull:-
        [_, _| _] = PntList,
        !,
        LeftPoint = list::minimum(PntList),
        RestPntList = list::remove(PntList, LeftPoint),
        % сортировка множества точек
        Ord1 = {(Pnt1, Pnt2) = compare(true, LeftPoint, Pnt1, Pnt2)},
        SortedList = list::sortBy(Ord1, RestPntList),
        % удаление точек, лежащих на одном луче, кроме дальней
        Ord2 = {(Pnt1, Pnt2) = compare(false, LeftPoint, Pnt1, Pnt2)},
        List = list::removeConsecutiveDuplicatesBy(Ord2, SortedList),
        ConvexHull = graham([LeftPoint | List]).
    createConvexHull(_PntList) = [].
    
% определение двух отношений порядка
class predicates
    compare: (boolean, pnt, pnt, pnt) -> compareResult.
    compare: (integer Det) -> compareResult.
    det: (pnt, pnt, pnt) -> integer.
clauses
    % точки, не лежащие на одном луче, сорт-ся в соотв. c det
    compare(_, LeftPnt, Pnt1, Pnt2) = compare(Det):-
        Det = det(LeftPnt, Pnt1, Pnt2),
        Det <> 0,
        !.
        
    % точки, лежащие на одном луче, различаются
    compare(true, pnt(X, Y), pnt(A, B), pnt(C, D)) = compare(
        % манхэттенское расстояние
        math::abs(A - X) + math::abs(B - Y),
        math::abs(C - X) + math::abs(D - Y)):- !.
    % точки, лежащие на одном луче, не различаются
    compare(_, _, _, _) = equal.
    
    compare(Det) = less:-
        Det > 0,
        !.
    compare(_) = greater.
    
    det(pnt(X, Y), pnt(A, B), pnt(C, D)) = 
        (A - X) * (D - Y) - (B - Y) * (C - X).
        
% обход Грэхема
class predicates
    graham: (pnt*) -> pnt*.
    graham: (pnt* List, pnt* Stack) -> pnt*.
clauses
    graham([LeftPnt, Pnt | List]) = graham(List, [Pnt, LeftPnt]):- !.
    graham(List) = List.
    
    graham([K |List], [M, N |Stack]) = graham([K |List], [N |Stack]):-
        det(M, N, K) > 0,
        !.
    graham([K | List], Stack) = graham(List, [K | Stack]).
    graham([], Stack) = Stack.
Листинг 2.12. Определение предиката построения выпуклой оболочки

Напомним, что выпуклая оболочка строится при нажатии кнопки convexhull панели инструментов. Она соединяет центры крупных "точек" — вершин выпуклой оболочки (рис. 2.12 рис. 2.12).

Выпуклая оболочка множества "крупных" точек

Рис. 2.12. Выпуклая оболочка множества "крупных" точек

Упражнение. Создайте на панели инструментов новую кнопку и добавьте процедуру сохранения рисунка в bmp-файле при нажатии на эту кнопку. Указание. Диалоговое окно "Сохранить как" вызывается следующим образом:

FileName = vpiCommonDialogs::getFileName("*.bmp",
    ["Файл bmp", "*.bmp"], "Сохранить как", [dlgfn_Save], "", _).

Получить рисунок из заданной прямоугольной области окна Rectangle и сохранить его в bmp-файле можно с помощью предикатов pictGetFromWin/2 и pictSave/2:

Picture = vpi::pictGetFromWin(getVpiWindow(), Rectangle),
    vpi::pictSave(Picture, "mypicture.bmp").

В данном случае необходимо учесть размеры панели инструментов. Прямоугольник TBRect, который занимает панель инструментов, возвращает предикат getRect/3:

vpiToolbar::getRect(getVpiWindow(), vpiToolbar::tb_left, TBRect).

Всплывающее меню и меню окна

Добавим к окну paintForm всплывающее меню и меню окна.

Создание всплывающего меню

Создадим всплывающее меню. Для этого выделим папку paintForm дерева проекта и выберем команду всплывающего меню New In Existing Package. В окне Create Project Item выделим элемент Menu и назовем его paintPopUpMenu.

Создадим меню, показанное на рис. 2.13 рис. 2.13. Для пунктов меню важно выбрать идентификаторы, совпадающие с идентификаторами аналогичных кнопок панели инструментов (id_color, id_point, id_marker, id_eraser, id_clear и id_convexhull), чтобы использовать уже существующее определение предиката onMenuItem для обработки события выбора пункта контекстного меню.

Окно редактора paintPopUpMenu

Рис. 2.13. Окно редактора paintPopUpMenu

Всплывающее меню будет появляться при нажатии правой кнопки мыши. Поэтому в качестве первого предложения для предиката onMouseDown необходимо добавить следующее правило:

onMouseDown(_Source, Point, _, mouse_button_right):- !,
        Menu = vpi::menuGetRes(id_paintpopupmenu),
        menuPopUp(Menu, Point, align_Left).

Предикат menuGetRes/1 возвращает меню из файла ресурсов. Предикат menuPopUp/3 открывает контекстное меню в указанной точке. В третьем аргументе этого предиката указывается тип выравнивания строк. Всплывающее меню показано на рис. 2.14 рис. 2.14.

Всплывающее меню

Рис. 2.14. Всплывающее меню

Создание меню окна

Аналогично создадим меню окна и назовем его paintMenu. Идентификаторы пунктов меню необходимо назвать точно так же, как и ранее (рис. 2.15 рис. 2.15).

Окно редактора paintMenu

Рис. 2.15. Окно редактора paintMenu

Добавим меню в окно paintForm. Для этого откроем редактор формы paintForm и на вкладке свойств окна Properties в поле Menu выберем элемент paintMenu. В результате всякий раз, когда окно "Выпуклая оболочка" будет активным, вместо главного меню приложения будет отображаться меню окна (рис. 2.16 рис. 2.16).

Меню окна paintForm

Рис. 2.16. Меню окна paintForm

Изменение панели инструментов главного окна приложения

Создадим новую кнопку и поместим ее на панель инструментов главного окна приложения. При нажатии на эту кнопку будет открываться окно paintForm.

Подготовим изображение (bitmap) для кнопки размером 16 -> 16. С помощью элемента дерева проекта TaskWindow\Toolbars\ProjectToolbar.tb откроем редактор панели инструментов главного окна приложения. Добавим на панель кнопку так же, как это было описано ранее (Constant: id_paintForm). С помощью элемента taskWindow.win дерева проекта откроем эксперт окон, раскроем узлы Menu и ProjectToolbar, выделим элемент id_paintForm и добавим обработчик события нажатия на кнопку панели инструментов. Определим его так же, как и предикат onTestPaint (см. п. 2.1).

Добавление строки состояния. Изменение пиктограммы

Добавим строку состояния в окно paintForm. Для этого выделим узел paintForm дерева проекта, откроем окно Create Project Item, выделим элемент Toolbar и в поле Name напишем его название: paintStatusLine. В окне атрибутов панели инструментов укажем стиль в поле Style: Bottom. Панель инструментов, расположенная в левой части окна, перекроет часть строки состояния, поэтому в ее редакторе добавим сначала текстовый элемент (Static Text), ширина которого равна ширине панели инструментов paintToolbar. В окне атрибутов текста укажем ширину этого элемента в поле Text Width: 32. Поле Initial text сделаем пустым. После этого добавим элемент Context Sensitive Text (рис. 2.17 рис. 2.17).

Редактор строки состояния paintStatusLine

Рис. 2.17. Редактор строки состояния paintStatusLine

Заполним поля в окне атрибутов текста так, как показано на рис. 2.18 рис. 2.18:

  • Initial Text: <пустое поле>;
  • Text Width: 400;
  • Foreground Color: Green.
Редактор атрибутов текста

Рис. 2.18. Редактор атрибутов текста

Перед добавлением строки состояния в окно paintForm, создадим для этого окна новую иконку (пиктограмму). Выделим узел paintForm дерева проекта, откроем диалоговое окно Create Project Item, выделим элемент Icon и напишем в поле Name его название: paintIcon. Закроем появившееся окно атрибутов изображения. Создадим изображение в открывшемся графическом редакторе.

Наконец, изменим определение конструктора new/1 в имплементации класса paintForm следующим образом:

new(Parent):-
        formWindow::new(Parent),
        generatedInitialize(),
        %
        setIcon(idi_paintIcon),
        whenCreated({ :- paintStatusLine::create(getVpiWindow()) }).

Строка состояния окна показана на рис. 2.19 рис. 2.19.

Строка состояния окна paintForm

Рис. 2.19. Строка состояния окна paintForm

Редактирование меню

Включить пункт меню и добавить обработчик события выбора команды меню можно программным образом. Например, изменим в файле taskWindow.pro определение предиката onShow так, как показано ниже, а также добавим код, определяющий предикат myFileOpen:

clauses
    onShow(_, _CreationData) :-
        _MessageForm = messageForm::display(This),
        menuEnable(resourceIdentifiers::id_file_open, true),
        menuSetText(resourceIdentifiers::id_file_open, "Открыть"),
        addMenuItemListener(myFileOpen).
        
predicates
    myFileOpen : window::menuItemListener.
clauses
    myFileOpen(_, resourceIdentifiers::id_file_open):- !,
        vpiCommonDialogs::note("Hello!").
    myFileOpen(_, _).

В результате пункт меню File -> Open станет включенным, надпись изменится на "Открыть", и, при выборе данной команды меню, откроется диалоговое окно Note, в поле сообщения которого будет выведен текст "Hello!".

Меню нетрудно создать динамически (см. определение домена dynMenu). Устанавливает меню предикат menuSet/1.

Подробнее о редакторах ресурсов и, в частности, панели инструментов и пиктограмм, см. [14, [ 14 ] ].

Упражнения

2.1. Добавьте команду удаления изображения выпуклой оболочки.

2.2. Сделайте автоматической перерисовку выпуклой оболочки при изменении рисунка.

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

2.4. Создайте инструменты для рисования графа. Вершины и ребра графа должны записываться в базу данных. База данных должна сохраняться в файле на диске и загружаться из него.

< Лекция 1 || Лекция 2 || Лекция 3 >
Алексей Роднин
Алексей Роднин
Россия
Роман Гаранин
Роман Гаранин
Беларусь, Брест