- •Лабораторная работа № 5 тема:«Создание приложений с gui без среды guide»
- •Простой пример - приложение с кнопкой и осями
- •Задание размеров окна приложения и элементов интерфейса
- •Задание дополнительных параметров в функциях обработки событий
- •Скрытие указателей объектов приложения с gui
- •Получение указателей на объекты приложения в функциях обработки событий, функция guihandles.
- •Сохранение данных, полученных в функции обработки событий, функция guidata.
- •Обмен данными между функциями обработки событий. Обработка событий объектов, создаваемых приложением.
- •Сохранение объектов приложения для повторных запусков.
- •Рисование кривых мышью
- •Перемещение объектов на осях мышью
- •Приложение с панелью переключателей для zoom и pan
Обмен данными между функциями обработки событий. Обработка событий объектов, создаваемых приложением.
В этом разделе мы рассмотрим чуть более сложный пример, чем в предыдущем, поясняющий решение двух вопросов:
1) как организовать обмен данными и указателями на объекты приложения между функциями обработки событий; 2) как обрабатывать события, возникающие от тех объектов, которые были созданы в ходе работы приложения.
Обсудим эти вопросы на примере приложения plotlines, окно которого приведено на рис. 5.
Рис.
5. Окно приложения plotlines
Приложение plotlines содержит оси, область ввода для задания функции в соответствиями с правилами MATLAB и кнопку, нажатие на которую приводит к построению графика введенной функции на отрезке [-1,1]. График каждой новой функции добавляется на оси. Приложение plotlines запоминает все введенные формулы и при щелчке мышью по любой линии на осях в области ввода появляется соответствующая ей формула.
В основной функции приложения создаются:
окно приложения с тегом win;
оси c тегом axMain и свойством NextPlot, установленным в add, для добавления линий;
кнопка с тегом btnPlot и функцией BtnPlotCallback обработки события Callback;
область ввода текста с тегом edtFun.
Указатели на объекты приложения plotlines хранятся в структуре handles, которая создается в конце основной функции приложения при помощи функции guihandles. Напомним, что названия полей структуры handles совпадают с тегами объектов, т.е. handles.win содержит указатель на графическое окно приложения, handles.axMain - на оси, handles.btnPlot - на кнопку и handles. edtFun - на область ввода текста (не все указатели могут понадобиться, однако, лучше всегда при создании объектов приложения давать им уникальные теги).
Затем в структуру handles добавляется два поля:
Funs - для хранения введенных формул в массиве ячеек, каждая ячейка содержит строку с формулой;
Lines - для хранения указателей на построенные линии в векторе.
Начальными значениями этих полей должны быть пустые массивы: handles.Funs - пустой массив ячеек {}, handles.Lines - пустой числовой массив []. После добавления полей Funs и Lines в структуру handles и инициализации их значений вызывается функция guidata для сохранения структуры handles.
В функции BtnPlotCallback обработки события Callback кнопки вызывается функция guidata для записи в handles структуры данных приложения. Входным аргументом функции guidata должен быть указатель на объект приложения, в нашем случае это указатель на кнопку, который содержится в первом входном аргументе src функции BtnPlotCallback. Далее определяется номер новой линии графика (число построенных линий, т.е. длина массива указателей на линии handles.Lines, плюс один). В новую ячейку массива handles.Funs заносится содержимое строки ввода на текущий момент (указатель на нее находится в handles.edtFun) и заданная функция визуализируется. Функция flpot не строит график, она только вычисляет функцию на отрезке [-1, 1] и записывает абсциссы и ординаты в массивы x и y. Объект линия получается в результате работы функции plot, которая возвращает указатель на линию. Он добавляется в новый элемент массива handles.Lines.
Для линии указывается, что ее событие ButtonDownFcn, которое возникает при щелчке мышью по линии, обрабатывается функцией LineButtonDownFcn (эта функция будет одна для всех линий). В функции LineButtonDownFcn значение первого входного аргумента src будет указателем на линию. Поэтому необходимо найти номер элемента массива handles.Lines, совпадающего с src, и в строку ввода поместить содержимое ячейки массива handles.Funs с тем же самым номером.
function plotlines
% создаем графическое окно с тегом win
hF = figure('Name', 'plotlines', 'NumberTitle','off',...
'MenuBar', 'none', 'Units', 'characters',...
'Position', [10 10 100 30], 'Tag', 'win');
% создаем оси с тегом axMain, свойство NextPlot = 'add' для добавления новых линий
axes('Position', [0.1 0.15 0.8 0.8], 'Tag', 'axMain',...
'NextPlot', 'add');
% создаем область ввода с тегом edtFun
uicontrol('Style', 'edit', 'Units', 'normalized',...
'Position', [0.02 0.02 0.3 0.06],...
'BackgroundColor', 'w',...
'Tag', 'edtFun');
% создаем кнопку с тегом btnPlot и функцией BtnPlotCallback обработки
% события Callback
uicontrol('Style', 'pushbutton', 'Units','normalized',...
'Position', [0.35 0.02 0.1 0.06],...
'String', 'Plot', 'Callback', @BtnPlotCallback,...
'Tag', 'btnPlot');
% записываем указатели на объекты в структуру handles
handles = guihandles(hF);
% добавляем в структуру handles поле Funs для записи строк с формулами
% в массив ячеек и присваиваем ему пустой массив ячеек
handles.Funs = {};
% добавляем в структуру handles поле Lines для записи указателей на линии
% и присваиваем ему пустой числовой массив
handles.Lines=[];
% сохраняем обновленную структуру
guidata(hF, handles)
% скрываем указатели
set(hF, 'HandleVisibility', 'callback');
function BtnPlotCallback(src, evt)
% функция обработки события Callback кнопки
% получаем структуру с указателями и данными
handles = guidata(src);
% определяем номер новой линии
k = length(handles.Lines)+1;
% заносим в новую ячейку поля Funs структуры handles строку с формулой
handles.Funs{k} = get(handles.edtFun, 'String')
% вычисляем значения функции на отрезке [-1,1]
[x,y] = fplot(handles.Funs{k}, [-1 1]);
% строим график функции, указатель на линию графика записываем в новую
% ячейку массива handles.Lines
handles.Lines(k) = plot(x,y,'LineWidth',3);
% связываем функцию LineButtonDownFcn с событием, которое возникает при
% нажатии кнопки мыши на линии
set(handles.Lines(k), 'ButtonDownFcn', @LineButtonDownFcn)
% сохраняем структуру handles
guidata(src, handles)
function LineButtonDownFcn(src, evt)
% обработка нажатия кнопки мыши на линии, в src - указатель
% на текущую линию, событие ButtonDownFcn которой обрабатывается
% получаем структуру handles с указателями и данными
handles = guidata(src);
% ищем номер текущей линии с указателем src
k = find(handles.Lines == src);
% записываем в строку ввода формулу, соответствующую текущей линии графика
set(handles.edtFun, 'String', handles.Funs{k})
Вообще говоря, этот пример приведен только для демонстрации обмена данными между функциями обработки событий. Приложение plotlines можно было запрограммировать проще. У объектов, в том числе и линий, есть свойство UserData, значением которого могут быть данные, ассоциированные с объектом. В нашем случае достаточно при построении линии задать соответствующую строку с формулой в качестве значения ее свойства UserData. В функции LineButtonDownFcn, обрабатывающей нажатие кнопки мыши на линии, значение свойства UserData текущей линии (указатель на нее - во входном аргументе src) записывается в область ввода. В таком варианте функция guidata не нужна - обмен данными между подфункциями производится при помощи свойства UserData объекта.
Вот листинг измененной функции с подфункциями.
function plotlines
% создаем графическое окно с тегом win
hF = figure('Name', 'plotlines', 'NumberTitle','off',...
'MenuBar', 'none', 'Units', 'characters',...
'Position', [10 10 100 30], 'Tag', 'win');
% создаем оси с тегом axMain, свойство NextPlot = 'add' для добавления новых линий
axes('Position', [0.1 0.15 0.8 0.8], 'Tag', 'axMain',...
'NextPlot', 'add');
% создаем область ввода с тегом edtFun
uicontrol('Style', 'edit','Units', 'normalized',...
'Position', [0.02 0.02 0.3 0.06],...
'BackgroundColor','w',...
'Tag', 'edtFun');
% создаем кнопку с тегом btnPlot и функцией BtnPlotCallback обработки
% события Callback
uicontrol('Style', 'pushbutton', 'Units','normalized',...
'Position', [0.35 0.02 0.1 0.06],...
'String', 'Plot', 'Callback', {@BtnPlotCallback},...
'Tag', 'btnPlot');
% скрываем указатели
set(hF, 'HandleVisibility', 'callback');
function BtnPlotCallback(src, evt)
% функция обработки события Callback кнопки
% получаем структуру указателей на объекты приложения
handles = guihandles(src);
% записываем в formula содержимое строки ввода
formula = get(handles.edtFun, 'String')
% строим график функции, заданной строкой formula
[x,y] = fplot(formula, [-1 1]);
hL = plot(x,y,'LineWidth',3);
% связываем функцию LineButtonDownFcn с событием, возникающим при нажатии
% кнопки мыши на линии
set(hL, 'ButtonDownFcn', @LineButtonDownFcn)
% записываем в свойство UserData линии строку с соответствующей ей формулой
set(hL, 'UserData', formula)
function LineButtonDownFcn(src, evt)
% обработка нажатия кнопки мыши на линии, в src - указатель
% на текущую линию, событие ButtonDownFcn которой обрабатывается
% получаем структуру handles с указателями и данными
handles = guihandles(src);
% считываем значение свойства UserData линии, оно содержит формулу
formula = get(src, 'UserData');
% записываем формулу в строку ввода
set(handles.edtFun, 'String', formula)
В MATLAB 7 структура графических объектов несколько изменилась по сравнению с предыдущими версиями. В частности, появились рисованные объекты, которые строятся высокоуровневыми графическими функциями (например, plot), в отличие от базовых, которые получаются при вызове низкоуровневых функций (например line). У рисованных объектов линий (Lineseries) есть свойство DisplayName, значением которого может быть строка (именно эта строка будет отображаться в легенде рядом с образцом линии при использовании функции legend). В нашем примере можно было задействовать свойство DisplayName вместо UserData.
