Главная страница
Навигация по странице:

  • Название процедуры Добавленный код 1 2

  • Матлаб. Информация о владельце фио Локтионова Оксана Геннадьевна


    Скачать 1.64 Mb.
    НазваниеИнформация о владельце фио Локтионова Оксана Геннадьевна
    АнкорМатлаб
    Дата07.02.2022
    Размер1.64 Mb.
    Формат файлаpdf
    Имя файлаMU_Vvedenie_v_matlab_LZNo_1-4.pdf
    ТипЛабораторная работа
    #354464
    страница4 из 6
    1   2   3   4   5   6

    Лабораторная работа №4
    Реализация вычислений с помощью графического интерфейса
    (GUI)
    Наличие графического интерфейса служит косвенным признаком коммерциализуемости программного обеспечения. По существу современные стандарты и пользовательские ожидания требуют присутствия GUI(Graphical User Interface) в любой программе. Разумеется, вопрос о том, является ли М-файл полноценной программой, остается дискуссионным – мы отвечаем на него положительно. В научно-исследовательской работе можно обходиться без каких-то оболочек, довольствуясь командной строкой, но лучше затратить сразу немного времени на создание оболочки, чтобы далее в цивилизованных условиях проводить вычислительные эксперименты и отладку содержания математических моделей.
    Описание работы по созданию GUI превосходно описано в мультимедийной справке: см. закладку Help-> Demos->MATLAB-
    >Creating Graphical User Interfaces или файл Macromedia Player
    \demos\CreatingaGUIwithGUIDE.swf. См. также раздел справки
    Getting Started->Creating Graphical User Interfaces. Существует три основных шага: зарисовка желаемого интерфейса (кнопочки, тестовые боксики и пр.) в редакторе GUIDE, генерация М-кода графической оболочки и модификация последнего и текста своей расчетной программы с целью их связывания. Последний шаг наиболее существенен. Отметим, что вызванной из графического интерфейса программе не возбраняется писать служебные сообщения по-прежнему в командное окно. Ниже рассмотрим простейший пример GUI-программы, вычисляющей сумму двух чисел A+B.

    61
    Рисунок 17 - Простейший пример GUI-программы
    Войдем в редактор - на главном меню. Уменьшим размеры будущего окна до 200*300 пиксел (пользуясь сеткой). Формат сетки можно задать через Tools->Grid and Rulers. Полезно установить флажок Snap Grid – «Выравнивание по сетке». Теперь отобразим два окна – Инспектора Свойств и Обозревателя Объектов (View->Property
    Inspector, Object Browser); это можно сделать и через иконки
    Каждая кнопка или флажок, перетаскиваемая на полотно с палитры слева, имеют такие свойства как имя (tag), цвет и вызываемую функцию. Менять свойства объекта можно либо через контекстное меню, связанное с ним, либо щелкая по его названию в древе на
    Обозревателе (свойства автоматически отображаются в Инспекторе).
    Изменим цвет на зеленый, имя на Summa. Добавим два текстовых бокса под именами ValueA и ValueB. Для этого перетащим первый из
    Edit Box слева, поменяем его цвета, tag и value. Щелкнув правой кнопкой мыши по нему, выберем Dublicate. На новом объекте изменим его tag и остальные параметры. Создадим статический текст
    – не путать имя и текстовую метку! Тем же способом создадим бокс

    62
    (имя – result) и надпись результат и две кнопки Push Button –
    «Закрыть» и «Пуск».
    Перейдем ко второму шагу. Сначала посмотрим опции – Tools-
    >GUI Option. Мы видим, что по умолчанию стоит «Сгенерировать и
    М-файл, и рисуночный файл *.fig». Поменяем только параметр масштабируемости окна (на proportional). Сохраним файлы под именем my1 (это делается за одну операцию), M-файл откроется автоматически. Готовясь к третьему шагу, подготовим простенький демофайл my1exec.m: function q=my1exec(p) q=p(1)+p(2); end
    Верхнюю часть кода my1.m-файла редактировать нецелесообразно, о чем нас предупреждают комментарии
    (рекомендуется внимательно прочитать эти сгенерированные автоматически комментарии). Внесем изменения согласно таблице 1.
    Таблица 1 – Вносимые изменения в программу
    Название
    процедуры
    Добавленный код
    1
    2
    my1_OpeningF
    cn my.A=0;my.B=0;setappdata(hObject,
    'my data'
    ,my);
    ValueA_Callb ack my=getappdata(handles.Summa,
    'mydata'
    ); my.A=str2double(get(hObject,
    'String'
    )); setappdata(handles.Summa,
    'mydata'
    ,my
    );
    ValueB_Callb ack my=getappdata(handles.Summa,
    'mydata'
    ); my.B=str2double(get(hObject,
    'String'
    )); setappdata(handles.Summa,
    'mydata'
    ,my
    );

    63
    Продолжение таблицы 1
    1
    2
    CloseMy_Call back rmappdata(handles.Summa,
    'mydata'
    ); delete(handles.Summa);
    Go_Callback my=getappdata(handles.Summa,
    'mydata'
    ); res=my1exec([my.A my.B]); set(handles.result,
    'String'
    ,num2str(
    res));
    Сделаем ряд замечаний. Во-первых, структура файла несколько отличается от обычного М-файла, приближаясь к Си-коду. Все подфункции написаны как бы параллельно, отражая специфику ориентированного на события программирования. Во-вторых, за небольшими уточнениями оказывается, что созданное окно тождественно объекту класса figure. В-третьих, за некоторыми исключениями каждому компоненту окна соответствуют две по названию связанные с ним процедуры – Callback и CreateFcn. Вторая функция автоматически вызывается при его создании (в языке Си аналогично «конструктору» объекта при его инициализации), а вторая – при наступлении некоторых событий, с ним связанных.
    Напомним, что событием в Windows называется нажатие клавиши, щелчок мыши в определенном месте экрана и прочее вызванное внешними причинами (например, пользователем) изменение среды.
    Полезно посмотреть Инспектор до и после сохранения М-файла, сравнить изменения полей/свойств, которые заканчиваются на Fcn; кроме того, мы видим, что можно определять не только две функции, связанные с компонентом, а более. В-четвертых, функция
    OpeningFcn, идущая вверху и относящаяся к figure целиком, является конструктором окна. Путем промежуточного переприсвоения указатель на нее выводится вовне (OutputFcn).
    Важнейшее значение имеет уникальная для конкретного окна
    GUI-структура, имя которой автоматически генерируется как handles.
    По сути это объект класса структура, поля которого являются указателями на все дочерние объекты, включая и сам figure. Она передается параметром в каждую процедуру и позволяет программисту изнутри обмениваться данными между дочерними

    64 компонентами окна. В нее можно записывать и пользовательские данные; поскольку значении переменных среды регистрируются циклически, то при изменении handles (путем прибавления лишнего поля, не обязательно указателя) простого присвоения недостаточно, а следует применять дополнительно команду guidata.
    Ключевой момент в создании GUI-программы – это организация обмена данными между расчетной программой и вызывающей ее оболочкой. В первую очередь для этого нужны те переменные, доступ к которым является общим. Один из путей слишком очевиден
    – обращение к глобальным переменным (см. маркер global). В разбираемом примере реализован путь добавления данных не путем наращения handles, а с помощью организации т.н. пользовательских данных. Они непосредственно не видны из различных функций
    (например, Callback-ов), но к ним можно получить доступ парой функций – getappdata, setappdata. Поэтому их удобнее всего инкапсулировать в одну структуру; тем не менее, при желании пользователь может ввести несколько таких структур (в отличие от
    GUI-данных, которые уникальны). В руководстве описан еще один способ – через свойство UserData, ассоциированным с каждым компонентом окна и фундаментальные функции set(handles.NNN,…) и get (здесь NNN – имя компонента, задаваемое в свойстве Tag).
    Подробнее разберем пример. Назначение GUI-программы – по числам, вводимым пользователем в два бокса, вычислить при нажатии на кнопку «Пуск» их сумму и отобразить ее в третьем боксе.
    При нажатии на кнопку «Закрыть» окно закрывается. В функции my1_OpeningFcn на лету создаем двупольную структуру my, затем данные в ней копируем/инкапсулируем в пользовательскую структур mydata. Эта структура впредь будет ассоциирована с figure, указатель на которую временно совпадает с hObject, но статически свопадает с handles.Summa. Следующие две строки таблицы описывают изменение пользовательских данных – функция Callback вызывается после «отщелкивания» курсора от бокса. Триада команд является типической: считывание пользовательских данных (Application Data) в локальную переменную, ее изменение и запись их обратно. Во второй команде по указателю hObject=handles.ValueA(B) мы получаем доступ к тексту, набранному пользователем в боксе, затем преобразуем строку в число. В предпоследней строке таблицы «для очистки совести» уничтожаем пользовательские данные и закрываем окно традиционной командой уничтожения динамической

    65 переменной по указателю. Последняя строка таблицы собственно и содержит то, ради чего писалась оболочка.
    Считываем пользовательские данные, числа А и В, вызываем параметрически файл my1exec.m с кодами расчета; результаты, возвращаемые в переменную res, напрямую подаются в свойство компонента result
    (изменение свойств происходит визуально в третьем боксе).
    Пример:
    1.
    Реализовать усеченную версию игры «О, счастливчик» в виде последовательности окон. Файл MS Excel содержит базу вопросов-ответов в формате колонок: А – вопрос, B – правильный ответ, СDE – неправильные ответы, F – начисляемые очки за вопрос.
    При решении примера вспомним, как мы поступали ранее при зарисовке графиков, создавая множество figure. Теперь наши возможности расширены за счет команд вызова стандартных типов окон – см. MATLAB-> Functions -- By Category-> Creating Graphical
    User Interfaces-> Predefined Dialog Boxes. И в данном случае не графика довлеет над расчетной программой, а наоборот.
    Сценарий программы таков:
    1)
    Спросить пользователя, использовать ли настройки по умолчанию?
    2)
    Yes – задать путь к новой базе, новый шрифт и вид окна ответов, затем показать шкалу прогресса процесса (waitbar);No – самим задать
    3)
    В цикле показывать вопросное окно, верность/неверность ответа и суммарный балл и предложение продолжить
    Соответственно после задания пробной базы в Excel (OLbase.xls) представим макет программы Olucky.m:
    %Primary function function
    Olucky=Olucky()
    %Default param function
    [OLbasepath,OLFont,OLStyle]=OluckyDef()
    OLbasepath=0,OLFont=1,OLStyle=2, end

    66
    %New param function
    [OLbasepath,OLFont,OLStyle]=OluckyNew()
    OLbasepath=0,OLFont=-1,OLStyle=-2, end
    %Quest/Answer function
    Result=OluckyRes(Number,Summa)
    Result=9, end
    [a,b,c]=OluckyDef();a,
    [a,b,c]=OluckyNew();b, c=OluckyRes(3,4),
    Olucky=777; end
    Разумеется, пока синтаксически верная программа ничего не делает.
    Начнем заполнять подфункцию
    OluckyNew.
    Соответствующие GUI-вызовы основаны на uigetfile, uisetfont, msgbox: function
    [OLbasepath,OLFont1,OLStyle]=OluckyNew()
    [f,fpath]=uigetfile(
    '*.xls'
    ,
    'Где вопросы?'
    ,
    'C:\'
    );
    OLbasepath=[fpath,f]; button=questdlg(
    'Ответы по вертикали?'
    ,
    'Кнопки или списки...'
    ,
    'Да'
    ,
    'Нет'
    ,
    'Да'
    ); if
    (strcmp(button,
    'Да'
    )), OLStyle=true; else
    OLStyle=false; end
    ; h=msgbox(
    'Сейчас вам будет предложено выбрать шрифт'
    ,
    'Или шрифты'
    ,
    'help'
    ,
    'non- modal'
    );waitfor(h);OLFont1=uisetfont(OLFont); end
    Отметив, что переменная OLFont есть структура, пишем
    OluckyDef function
    [OLbasepath,OLFont,OLStyle]=OluckyDef()
    OLbasepath=
    'OLbase.xls'
    ;OLStyle=true;

    67
    OLFont=struct(
    'FontName'
    ,
    'Times New
    Roman'
    ,
    'FontUnits'
    ,
    'points'
    ,
    'FontSize'
    ,12,
    'FontWeight'
    ,
    'normal'
    ,
    'FontAngle'
    ,
    'i talic'
    ); end
    Чтобы код был исполняемым, произведем замену в запускающей части кода. button=questdlg(
    'Применить параметры по умолчанию?'
    ,
    'Вы ленивы?'
    ,
    'Да'
    ,
    'Нет'
    ,
    'Да'
    );
    [OLbasepath,OLFont,OLStyle]=OluckyDef(); if
    (strcmp(button,
    'Нет'
    )),
    [OLbasepath,OLFont,OLStyle]=OluckyNew(); end
    ; xlsread(OLbasepath,-1);
    Составим новую функцию чтения xls-данных и соответственно заменим последнюю строку на вызов этой вложенной фукнции:
    %Read xls function
    [N,Quest,Ans,Weight]=OLread(path)
    [Weight,NQuestAns]=xlsread(OLbasepath,
    'A:F'
    );
    N=size(NQuestAns);N=N(1);
    Quest=NQuestAns(2:N,1);Ans=NQuestAns(2:N,2:5);N=N-
    1; end
    ….
    [N,Quest,Ans,Weight]=OLread(OLbasepath);
    Теперь обратимся к функции, которая должна выполняться в цикле - OluckyRes. Если пользователь решает прервать игру, то она возвращает -1.
    %Quest/Answer function
    Result=OluckyRes(Number,Summa) message={{};{};{}}; message{1}=strcat(
    'После '
    ,int2str(Number(1)),
    '-го вопроса'
    );

    68 message{2}=strcat(
    'у вас в кошельке '
    ,int2str(Summa),
    ' тысяч рублей'
    ); message{3}=
    'Не пора ли забрать деньги?'
    ; button=questdlg(message,
    'Отвечать иль не отвечать?'
    ,
    'Да'
    ,
    'Нет'
    ,
    'Нет'
    );
    %QuestN+AnsN+TrueAnsN - все, что знаем о N-м вопросе
    AnsN={Ans{Number(1),Number(2)},Ans{Number(1),Numbe r(3)},
    Ans{Number(1),Number(4)},Ans{Number(1),Number(5)}}
    ;
    QuestN=Quest{Number(1)};TrueAnsN=Ans{Number(1),1};
    WN=Weight(Number(1)); if
    (strcmp(button,
    'Да'
    )),
    Result=-1; elseif
    (OLStyle)
    [ind,ok]=listdlg(
    'ListString'
    ,AnsN,
    'SelectionMode'
    ,
    'single'
    ,
    'PromptString'
    ,QuestN,
    'ListSize'
    ,[400 100]); if
    (ok==0), ind=1; end
    ; if
    (strcmp(AnsN{ind},TrueAnsN))
    Result=Summa+WN; else
    Result=Summa; end else message=strvcat(
    'Мудрецы говорят:'
    ,AnsN{1},
    'Соглашаясь, нажмите крестик'
    ); button=questdlg(message,QuestN,AnsN{2},AnsN{3},Ans
    N{4},AnsN{3}); if
    (strcmp(button,
    ''
    )), button=AnsN{1}; end
    ; if
    (strcmp(button,TrueAnsN))
    Result=Summa+WN;

    69 else
    Result=Summa; end
    ; end
    ; end
    По параметру OLStyle определяем, что применяем – listdlg или questdlg. Перед этим, однако, загружаем из основной базы данных вопрос под номером QuestN и соответствующие ответы и цену ответа. Если ответ оказывается правильным, то сумма приращивается на цену ответа, выводясь в переменную Result.
    После того, как мы определились с реализацией опроса по некоторому вопросу из базы данных, нужно написать функцию сортировки ответов (чтобы пользователь не привык к правильности первого варианта).
    %SortAnswer function
    Num=OluckySort(Num) for i=1:20, ind=floor(1+4*rand(1));ind1=floor(1+4*rand(1)); v=Num(ind1);Num(ind1)=Num(ind);Num(ind)=v; end end
    Теперь в основное тело программы осталось добавить цикл.
    %Number - номер вопроса по счету, номера ответов в колонке xls... если равен 1, то
    %правильный
    Summa=0;Numb=[1 2 3 4]; for n=1:N,
    Numb=OluckySort(Numb);Number=[n
    Numb];Result=OluckyRes(Number,Summa); if
    (Result<0), break
    , end
    ;
    Summa=Result; end
    ;
    Olucky=Summa,

    70
    Мы показали, что даже с помощью стандартных диалоговых окон в MATLAB можно создать нечто практически полезное.
    Разумеется, с помощью созданного самими интерфейса можно было бы сделать игру более элегантной и «отшлифованной».
    2.
    В масштабируемом окне создать интерфейс, содержащем большинство элементов палитры, с целью адаптировать его к программе MyTriangle (см. пример №3 Лабораторной работы №3, часть 1).
    3.
    Рисунок 18 – Создание интерфейса, содержащего большинство элементов палитры
    Нулевым шагом здесь является просмотр файта MyTriangle.m и зарисовка на бумаге эскиза будущего GUI-окна.
    Окно имеет четыре больших компонента: оси, панель выбора параметров треугольника (внутри нее не поместившаяся снизу подпанель
    «Площадь и углы»), панель
    «Медианы/
    Биссектрисы/Высоты» и панель «Параметры графика». Сценарий таков.

    71
    Пользователь задает длины сторон треугольника, причем третью он может задать через полосу прокрутки. Дополнительно в развертывающемся меню он может выбрать тип треугольника, если не хочет вводить лишних сторон (например, если треугольник прямоугольный). Три «галочки» даны, если он захочет поменять порядок следования сторон. «Утопленная» кнопка с надписью «abc» играет роль четвертой галочки на случай, если треугольник задается через две стороны и угол между ними. После этого нажимается кнопка «Задать треугольник», он решается, и полупустые боксы ниже заполняются значениями углов, площади, медиан и т.п. При этом также сам треугольник рисуется на осях. Затем пользователь может перейти к более сложным задачам, связанным с расчетом площадей при разных длинах сторон – это последняя панель. Здесь он выбирает: какой тип графика ему нужен, нужно ли отображать линии сетки и внешний бокс, способ задания пределов изменения.
    Разумеется, все завершает кнопка «Построить».
    Отметим некоторые особенности. Для масштабируемости окна выбрать GUI-options свойство Resize behaviour как Propotional до его сохранения Для установки размеров окна нужно в его (figure)
    Инспекторе свойств задать: Units=pixels, Position=[100, 100, 800, 600].
    Для задания позиций popup-меню и/или Listbox нужно в Инспекторе найти свойство String и щелкнуть по пиктограмме
    . Все панели, кроме панели «Тип», имеют тип Panel; указанная же панель имеет тип buttongroup. Для создания меню окна необходимо исполнить пункт меню GUIDE Tools->Menu Editor. Пункты меню «Сохранить->bmp» и
    «Сохранить->jpg» добавлены с тем, чтобы пользователь мог сохранять картинку, изображенную на осях. Заметим, что можно было поступить более просто, выведя обычное меню окна figure
    (проставив в Инспекторе его свойств Menubar=figure), но гибче составить свое меню. Что касается создания панелей инструментов, тосредствами GUIDE это невозможно, однако, в режиме командной строки можно использовать команду uitoolbar.
    Что касается зарисовки «парадного треугольника», то здесь пришлось программировать. Мы выбрали несколько более сложный путь, пренебрегая советом писать код в функцию OpeningFcn окна.
    Вместо этого, не найдя функции CreateFcn для осей axes1 в сгенерированном коде MyTriangle1.m, нужно создать ее следующим универсальным способом – нужно выделить в GUIDE оси, в контекстном меню выбрать View->CreateFcn; при этом во-первых, в

    72
    М-файл дописывается кусок кода, и во-вторых, связь с этим фрагментом задается явно внутри fig-файла (посмотрите в
    Инспекторе свойсв для axes1 поле CreateFcn). Осталось только для верности сохранить оба файла – это делается стандартным способом.
    В сгенерированный фрагмент кода необходимо дописать несколько строк, причем обратите внимание на последнюю строчку.
    Ее присутствие вызвано тем, что структура handles заполняется указателями только после исполнения всех CreateFcn от дочерних объектов окна. function axes1_CreateFcn(hObject, eventdata, handles) x=[1 3 5 1];y=[1 4 2 1];plot(x,y); axis([0 6 0 6]);axis manual
    ;grid on
    ; handles.axes1=hObject;guidata(gcf, handles);
    Заполним сразу и код-обработчик пункта меню «Сохранить».
    Для примера рассмотрим сохранение bmp-файла (для простоты он сохраняется с именем photo в текущий каталог, легко можно было бы сделать сохранение гибче – с помощью диалоговых окон). Итак, читается указатель на оси, вычисляется абсолютная позиция осей axes1, из-за особенностей фукнции getframe нам необходимо немного расширить прямоугольную область, чтобы в «снимок» уместились шкалы осей, поэтому немного изменяем вектор rect. Далее связка трех функций: снятия снимка в переменную photo, затем ее перекодировка (с расщеплением) в пиксельный образ и сохранение последнего в файл. Написание Callback-а для подпункта jpg очевидно. function bmp_Callback(hObject, eventdata, handles) h=handles.axes1;rect=get(h,
    'Position'
    );rect1=get(h
    ,
    'OuterPosition'
    ); rect=[rect1(1)-rect(1) rect1(2)-rect(2) rect1(3) rect1(4)]; photo=getframe(h,rect);[photo,cmp]=frame2im(photo)
    ; imwrite(photo,
    'photo.bmp'
    ,
    'bmp'
    );beep;
    4.
    Реализовать «решение треугольника» и более сложные функции в GUI-программе

    73
    Помимо fig-файла будем использовать три M-файла:
    MyTriangle1.m (он уже сгенерирован и является управляющим),
    MyTriangle0exec.m (копия MyTriangle.m, решающая единичный треугольник) и MyTriangle1exec.m (здесь проводятся все действия по построению графиков, включая, например, параметрические расчеты кривых). Управляющий файл, разумеется, будет содержать прикладные данные, но не следует «втаскивать» в них какие-то многомерные массивы, нужные для построения графиков – достаточно просто передать при вызове исполняемых файлов указатель на те оси, в пределах которых и будут строиться кривые.
    Начнем с изначальной установки данных приложения. Добавим в функцию MyTriangle1_OpeningFcn GUI-файла блок:
    %Задание данных приложения mt=struct(
    'Square'
    ,6,
    'Sides'
    ,[3 4 5],
    'Angles'
    ,[0 0 90],
    'Medians'
    ,[1 1 1],
    'Bisectors'
    ,[2 2 2],
    'Heights'
    ,[3 3 3]); setappdata(hObject,
    'MTriangle'
    ,mt);
    Обратимся к панели задания треугольника и запишем обработчики текстовых боксов через View->Callbacks (поскольку боксы находятся на панели, то, к сожалению, функции вызова для них не были сгенерированы ранее, но, тем не менее, как не трудно убедиться, указатели на каждый бокс присутствуют в структуре hahdles). Например, для бокса, соответствующего стороне b: function edit2_Callback(hObject, eventdata, handles) mt=getappdata(handles.output,
    'MTriangle'
    ); mt.Sides(2)=str2double(get(hObject,
    'String'
    )); setappdata(handles.output,
    'MTriangle'
    ,mt);
    Что касается стороны с, то вначале запишем обработчик для полосы прокрутки: function slider1_Callback(hObject, eventdata, handles)

    74 mt=getappdata(handles.output,
    'MTriangle'
    ); pos=get(hObject,
    'Value'
    );flag=get(handles.togglebu tton1,
    'Value'
    ); if
    (flag) pos1=abs(mt.Sides(1)- mt.Sides(2));pos2=mt.Sides(1)+mt.Sides(2); pos=pos1+pos*(pos2-pos1);mt.Sides(3)=pos; else pos=180*pos;mt.Angles(3)=pos; end
    ; set(handles.edit3,
    'String'
    ,num2str(pos)); setappdata(handles.output,
    'MTriangle'
    ,mt);
    Первая ветка условного оператора соответствует ненажатой кнопке «abc» (возвращаемое ею значение равно 1) и ситуации ввода трех сторон. Отметим, что мы не стали принудительно изменять для слайдера его предельные значения (они равны 0 и 1).
    Обработчик для третьего бокса выглядит так: function edit3_Callback(hObject, eventdata, handles) mt=getappdata(handles.output,
    'MTriangle'
    ); pos=str2double(get(hObject,
    'String'
    ));flag=get(han dles.togglebutton1,
    'Value'
    ); if
    (flag) mt.Sides(3)=pos; pos1=abs(mt.Sides(1)- mt.Sides(2));pos2=mt.Sides(1)+mt.Sides(2); pos=(pos-pos1)/(pos2-pos1); else mt.Angles(3)=pos;pos=pos/180; end
    ; set(handles.slider1,
    'Value'
    ,pos); setappdata(handles.output,
    'MTriangle'
    ,mt);
    Напишем обработчик для кнопки-триггера (начальное значение установим как 1). Обратим внимание, что поле Value обновляется автоматически, и мы лишь сменяем надпись и цвет кнопки:

    75 function togglebutton1_Callback(hObject, eventdata, handles) q=get(hObject,
    'Value'
    ); if
    (

    q) set(hObject,
    'String'
    ,
    'abC'
    ); set(hObject,
    'BackgroundColor'
    ,[1 1 0.5]); else set(hObject,
    'String'
    ,
    'abc'
    ); set(hObject,
    'BackgroundColor'
    ,[0.5 1 0.5]); end
    ;
    Напишем фрагмент обработчика всплывающего меню (с учетом показаний «галочек» - здесь есть некая избыточность). Выбранный тип треугольника влияет на показания полей боксов сторон a,b,c, но еще не на сами данные приложения. function popupmenu1_Callback(hObject, eventdata, handles) pos=get(hObject,
    'Value'
    );mt=getappdata(handles.out put,
    'MTriangle'
    ); flags=[get(handles.checkbox1,
    'Value'
    ) get(handles.checkbox2,
    'Value'
    ) get(handles.checkbox3,
    'Value'
    )], switch
    (pos) case
    1 side=1;
    for i=1:3, if flags(i) side=mt.Sides(i); end
    ; end
    ; set(handles.edit1,
    'String'
    ,num2str(side)); set(handles.edit2,
    'String'
    ,num2str(side)); set(handles.edit3,
    'String'
    ,num2str(side)); case
    4 side=(mt.Sides(1)^2+mt.Sides(2)^2)^0.5; set(handles.edit3,
    'String'
    ,num2str(side)); otherwise
    ; end
    Теперь перейдем к обработке кнопки запуска. Имеющиеся данные, если поля менялись, должны составить данные приложения.
    Структуру данных MTriangle надлежит также дозаполнить вызовом уже расчетных процедур. Затем результаты следует отобразить в

    76 боксах панели «Медианы/…» и построить треугольник. function pushbutton1_Callback(hObject, eventdata, handles)
    % hObject handle to pushbutton1 (see GCBO)
    % eventdata reserved - to be defined in a future version of MATLAB
    % handles structure with handles and user data
    (see GUIDATA)
    %Фиксация входных данных mt=getappdata(handles.output,
    'MTriangle'
    ); ra=str2num(get(handles.edit1,
    'String'
    ));mt.Sides(1
    )=ra; rb=str2num(get(handles.edit2,
    'String'
    ));mt.Sides(2
    )=rb; rc=str2num(get(handles.edit3,
    'String'
    )); if
    (get(handles.togglebutton1,
    'Value'
    )) mt.Sides(3)=rc; else mt.Angles(3)=rc; end rflag=get(handles.togglebutton1,
    'String'
    );
    %Решение треугольника
    [S,aA,bB,cC,Ma,Mb,Mc]=MyTriangle0exec(ra,rb,rc,rfl ag,
    'med'
    ); mt.Sides=[aA(1) bB(1) cC(1)];mt.Angles=[aA(2) bB(2) cC(2)]; mt.Medians=[Ma Mb Mc];mt.Square=S;
    [S,aA,bB,cC,Ma,Mb,Mc]=MyTriangle0exec(ra,rb,rc,rfl ag,
    'bis'
    );mt.Bisectors=[Ma Mb Mc];
    [S,aA,bB,cC,Ma,Mb,Mc]=MyTriangle0exec(ra,rb,rc,rfl ag,
    'hhh'
    );mt.Heights=[Ma Mb Mc]; setappdata(handles.output,
    'MTriangle'
    ,mt);
    %Обновление полей set(handles.edit3,
    'String'
    ,num2str(mt.Sides(3))); set(handles.edit4,
    'String'
    ,num2str(mt.Square));h=0
    ; for i=1:3, command=[
    'h=handles.edit'
    int2str(4+i)
    ';'
    ];eval(command);

    77 set(h,
    'String'
    ,num2str(mt.Angles(i))); command=[
    'h=handles.edit'
    int2str(7+i)
    ';'
    ];eval(command); set(h,
    'String'
    ,num2str(mt.Medians(i))); command=[
    'h=handles.edit'
    int2str(10+i)
    ';'
    ];eval(command); set(h,
    'String'
    ,num2str(mt.Bisectors(i))); command=[
    'h=handles.edit'
    int2str(13+i)
    ';'
    ];eval(command); set(h,
    'String'
    ,num2str(mt.Heights(i))); end
    ;
    %Построение треугольника x=zeros(1,4);y=x;x(2)=mt.Sides(1);x(3)=x(2)- mt.Sides(2)*cosd(mt.Angles(3)); y(3)=y(2)+mt.Sides(2)*sind(mt.Angles(3)); x=x+abs(mt.Sides(1)- mt.Sides(2))*ones(1,4);y=y+ones(1,4); axes(handles.axes1);cla;plot(x,y,
    'g-
    '
    );p=sum(mt.Sides)/2+1; set(gca,
    'Color'
    ,[1 1 0.5]);axis([0 p 0 p]);grid on
    ;box on
    ;
    Чтобы как-то сократить число однотипных выводов в боксы нижней панели, мы воспользовались функцией eval. При построении треугольника удобно вначале сделать текущими осями оси axes1 (см. команду axes). Для лучшего вида окна добавим в функцию
    OpeningFcn начальные значения popup-меню и «галочек», а также начального значения в группе радиокнопок «Тип»:
    % Доустановка параметров set(handles.popupmenu1,
    'Value'
    ,6); set(handles.checkbox1,
    'Value'
    ,1); set(handles.radiobutton1,
    'Value'
    ,1); set(handles.uipanel4,
    'UserData'
    ,1); set(handles.listbox1,
    'UserData'
    ,[0 5]);
    Работа с панелями кнопок имеет особенности. Хотя существуют указатели на каждую радиокнопку отдельно, равно как и указатель на группу в целом, функция обработчик составляется на группу и имеет название не Callback, а SelectionChangeFcn (это название проявляется

    78 в контекстном меню View Инспектора свойств для группы). В частности, такой механизм гарантирует единственность выбранного варианта. Шаблон функции перепишем из примера: function uipanel4_SelectionChangeFcn(hObject, eventdata, handles)
    % hObject handle to uipanel4 (see GCBO)
    % eventdata reserved - to be defined in a future version of MATLAB
    % handles structure with handles and user data
    (see GUIDATA)
    switch get(hObject,
    'Tag'
    )
    % Get Tag of selected object case
    'radiobutton1'
    set(handles.uipanel4,
    'UserData'
    ,1); case
    'radiobutton2'
    set(handles.uipanel4,
    'UserData'
    ,2); case
    'radiobutton3'
    set(handles.uipanel4,
    'UserData'
    ,3); case
    'radiobutton4'
    set(handles.uipanel4,
    'UserData'
    ,4); end
    Здесь нам важно сохранить выбор в какой-то переменной; удобным путем представляется использование такого способа сохранения данных, как поле UserData, в нашем случае, принадлежащего объекту buttongroup.
    Теперь настало время создать второй исполняемый файл
    MyTriangle.m. Достаточно будет вызывать его с 4-мя параметрами: hax – указатель на оси (handles.axes1), mt – номер процедуры, sides – вектор-строка со сторонами треугольника, limits – вектор пределов изменения (его трактовка зависит от функции). Тело файла, разумеется, содержит 4 вложенных функции, вызываемых из главной через оператор switch. function mt=MyTriangle1exec (hax,mt,sides,limits) function
    MT1=MT1()
    MT1=0; end

    79 function
    MT2=MT2()
    MT2=0; end function
    MT3=MT3()
    MT3=0; end function
    MT4=MT4()
    MT4=0; end switch mt case
    1
    MT1() case
    2
    MT2() case
    3
    MT3() case
    4 end mt=0; end
    Некоторым видоизменением решенного ранее примера №5-10
    Работы №2 (часть 2) легко наполнить тела трех вложенных функций.
    Эффективным оказывается не циклический вызов MyTriangle0exec.m, а векторный вызов файла geroncore.m.
    Перед оператором switch нужно расположить две команды, стирающие с осей прежние графики и устанавливающие оси на GUI- окне текущими: axes(hax);cla reset
    ; function
    MT1=MT1() hold on
    ; a=linspace(limits(1),limits(2),21);c=sides(3); b=min([abs(c-limits(1)) abs(c-limits(2))]); b=linspace(b,c+limits(2),8);MyColor=[
    'y'
    'm'
    'c'
    'r'
    'g'
    'b'
    'w'
    'k'
    ];
    Hlines=zeros(1,8); for j=1:8,

    80
    Hline=plot(a,geroncore(a,b(j),c));
    Hlines(j)=Hline;
    Lname=strcat(num2str(j),
    ': b=
    '
    ,num2str(b(j))); set(Hlines(j),
    'Color'
    ,MyColor(j),
    'DisplayName'
    ,
    Lname); end
    ; legend(
    'show'
    );xlabel(
    'a'
    );ylabel(
    'S(a)'
    );axis on
    ;axis tight
    ;hold off
    ;
    MT1=0; end function
    MT2=MT2() a=linspace(limits(1),limits(2),21);b=linspace(limi ts(3),limits(4),21);
    [X,Y]=meshgrid(a,b);contourf(X,Y,geroncore(X,Y,sid es(3)));colorbar;
    Sname=strcat(
    'S(a,b) at c=
    '
    ,num2str(sides(3)));title(Sname); xlabel(
    'a'
    );ylabel(
    'b'
    );axis on
    ;axis tight
    ;
    MT2=0; end function
    MT3=MT3() a=linspace(limits(1),limits(2),21);b=linspace(limi ts(3),limits(4),21);
    [X,Y]=meshgrid(a,b);surf(X,Y,geroncore(X,Y,sides(3
    )));colorbar;
    Sname=strcat(
    'S(a,b) at c=
    '
    ,num2str(sides(3)));title(Sname); xlabel(
    'a'
    );ylabel(
    'b'
    );zlabel(
    'S(a,b)'
    );axis on
    ;axis tight
    ;
    MT3=0; end function
    MT4=MT4()

    81 a=linspace(limits(1),limits(2),101);b=linspace(lim its(3),limits(4),101); c=linspace(limits(5),limits(6),101);[X,Y]=meshgrid
    (a,b);
    S=geroncore(a(50),b(50),c(50));MM=[];
    Hsur=surf(X,Y,S*ones(101),
    'LineStyle'
    ,
    'none'
    );colo rbar; colormap jet
    ; axis manual
    ;axis([a(1) a(101) b(1) b(101) 0 2*S+2]);title(
    ''
    ); xlabel(
    'a'
    );ylabel(
    'b'
    );zlabel(
    'S(a,b)'
    );grid on
    ;box on
    ; rect=get(hax,
    'Position'
    );rect1=get(hax,
    'OuterPosit ion'
    ); rect=[rect1(1)-rect(1) rect1(2)-rect(2) rect1(3) rect1(4)]; for t=c,
    [S,rect1]=sprintf(
    'Время С= %2.3f сек'
    ,t);title(S); set(Hsur,
    'ZData'
    ,geroncore(X,Y,t)); drawnow;
    MM=[MM,getframe(hax,rect)]; end
    ; movie2avi(MM,
    'mt4.avi'
    ,
    'fps'
    ,2);
    MT4=0; end
    Вернемся к GUI-файлу, точнее, к обработчику списка на панели
    «Параметры графика». Содержательную часть мы можем либо оставить для Callback-a кнопки пуска, либо частично обработать данные здесь же, используя вновь UserData для списка. Выбирая второе, будем иметь для обработчика: function listbox1_Callback(hObject, eventdata, handles) flag=get(hObject,
    'Value'
    );mt=getappdata(handles.ou tput,
    'MTriangle'
    );

    82 switch flag case
    1 a=mt.Sides(1);a=[a/2 2*a];b=mt.Sides(2);b=[b/2 2*b]; c=mt.Sides(3);c=[c/2 2*c]; case
    2 a=[0 5];b=[0 5];c=[0 5]; case
    3 a=[0 1];b=[0 1];c=[0 1]; end
    ; flag=get(handles.uipanel4,
    'UserData'
    ); switch flag case
    1 set(hObject,
    'UserData'
    ,a); case
    2 set(hObject,
    'UserData'
    ,[a b]); case
    3 set(hObject,
    'UserData'
    ,[a b]); case
    4 set(hObject,
    'UserData'
    ,[a b c]) end
    ;
    Теперь с обработчиком кноки пуска стало проще. Заметим, однако, что это сделало работу GUI-интерфейса менее корректной – ведь мы стали зависимыми от порядка нажатия на группу переключателей и список. Более правильный путь состоит в том, чтобы опрос всех параметров (переключателей, «галочек» и пр.) осуществлялся бы обработчиком кнопки пуска. function pushbutton2_Callback(hObject, eventdata, handles) mt=getappdata(handles.output,
    'MTriangle'
    ); hax=handles.axes1;mt_=get(handles.uipanel4,
    'UserDa ta'
    ); sides=mt.Sides;limits=get(handles.listbox1,
    'UserDa ta'
    ); mt=MyTriangle1exec(hax,mt_,sides,limits);
    Осталось только дописать простенький обработчик проверочного бокса «Сетка» и оставить ремарку пользователю:

    83 function checkbox4_Callback(hObject, eventdata, handles) grid; function figure1_DeleteFcn(hObject, eventdata, handles) disp(
    'Приятных сновидений'
    )
    1   2   3   4   5   6


    написать администратору сайта