Скачать .docx |
Реферат: калашник Борис Борисович разработка среды поддержки сценариев для генерации графических текстов, дипломная работа
Министерство образования и науки Российской федерации
Федеральное государственное автономное образовательное учреждение высшего профессионального образования
«Уральский федеральный университет
имени первого Президента России Б.Н. Ельцина»
Математико-механический факультет
Кафедра высокопроизводительных компьютерных технологий
Разработка среды поддержки сценариев для генерации графических текстов
"Допущен к защите" |
Квалификационная работа на степень бакалавра наук |
Екатеринбург
2011
РЕФЕРАТ
Калашник Борис Борисович РАЗРАБОТКА СРЕДЫ ПОДДЕРЖКИ СЦЕНАРИЕВ ДЛЯ ГЕНЕРАЦИИ ГРАФИЧЕСКИХ ТЕКСТОВ, дипломная работа:
Ключевые слова: ИНТЕРФЕЙС, МЕТАФОРА, МАНГА+АНИМЕ, СЦЕНАРИЙ.
Объект исследования – инструктивные информационные системы.
Цель работы – изучить и сделать инструктивную с многоуровневым интерфейсом информационную систему на базе метафоры манга+ аниме.
В процессе работы создания инструктивной информационной системы разделилось на два этапа: создание интерфейса и создание сценария.
В результате работы получилась инструктивная информационная система на примере мануала по обслуживанию персонального компьютера.
СОДЕРЖАНИЕ
Введение…………………………………………………………………………...4
1.Интерфейс……………………………………………………………………….7
2.Метафора………………………………………………………………………...9
2.1.Метафора интерфейса………………………………………………….9
2.2.Метафора визуализации……………………………………………....11
2.3.Метафора манга+аниме………………………………………………14
3.Постановка задачи……………………………………………………………..15
3.1.Описание задачи………………………………………………………15
4.Сценарий……………………………………………………………………….17
4.1.Сценарий – программа………………………………………………..18
4.2.Сценарий как граф…………………………………………………….19
4.3. Программирования сценария по образцам………………………....22
6.Заключение……………………………………………………………………..26
5.Список литературы…………………………………………………………….30
7.Приложение…………………………………………………………………….31
ВВВЕДЕНИЕ
В нынешнее время компьютеры уже плотно вошли в жизнь человека. Началась повсеместная компьютеризация любых видов деятельности и информации. Уже сегодня практически невозможно найти место, в котором компьютер не являлся бы важным ресурсом.
Заметим, что связи с компьютеризацией вся нужная нам информация переходит в “электронный вид”, что делает доступ к ней значительно удобнее, быстрее и доступнее. Вместе с этим появляется понятие информационной системы. Информационная система есть совокупность технического, программного и организационного обеспечения, а также персонала, предназначенная для того, чтобы своевременно обеспечивать надлежащих людей надлежащей информацией. Все информационные системы классифицируются следующим образом:
1. По архитектуре: настольные, в которых все компоненты находятся на одном компьютере, и распределённые, в которых компоненты распределены по нескольким компьютерам.
2. По степени автоматизации: автоматизированные, в которых требуется постоянное вмешательство персонала, и автоматические, в которых вмешательство персонала не требуется или требуется только эпизодически.
3. По характеру обработки данных: информационно-справочные, где целью системы является поиск и выдача информации в удобном виде, и обработки данных, в которых данные подвергаются обработке по сложным алгоритмам.
4. По сфере применения
5. По масштабности: персональная, предназначенная для решения некоторого круга задач одного человека, групповая, ориентированная на коллективное использование информации членами рабочей группы или подразделения, и корпоративная, в идеале охватывает все информационные процессы целого предприятия, достигая их полной согласованности.
В нашем проекте мы будет работать с информационно-справочным классом, так как это один из самых распространенных и широко используемых классов информационных систем. Примером информационной системы этого класса будет мануал. Руководство пользователя (англ. user guide или user manual), руководство по эксплуатации - документ, назначение которого — предоставить людям помощь в использовании некоторой системы, какой-либо вещи. Документ входит в состав технической документации на систему и, как правило, подготавливается техническим писателем. Отметим, что нас интересует мануал в электронном виде, рассмотрим их поближе. Для начала, видно, что любой электронный мануал является аналогом обычной книги, а нередко и точной копией. Современный электронный мануал содержит такие элементы как гиперссылка и вставки картинок. Гиперссылка (англ. hyperlink) в компьютерной терминологии - часть гипертекстового документа, ссылающаяся на другой элемент (команда, текст, заголовок, примечание, изображение) в самом документе. Гиперссылка улучшает понимание, даёт возможность углубления и расширения знаний. Картинки показывают наглядность и улучшают образное представление описываемых объектов. Всё это улучшает восприятие информации, но, не смотря на это, мануалы ещё можно и стоит улучшить, ведь это очень полезный класс информационных систем. Их можно улучшить, например, добавлением видео. Не смотря на всё, надо учитывать, чтобы мануал был понятен для всех. Мы считаем, что мануал и любая другая инструктивная информационная система, должен иметь сценарий, определяющий порядок и принцип работы всей системы.
Каждая информационная система или программа имеет свой определенный интерфейс, через который человек может взаимодействовать с компьютером, может формулировать и решать свои задачи. Но одними интерфейсами пользоваться удобно и просто, а другими сложно и долго.
Изучим и попытаемся сделать инструктивную с многоуровневым интерфейсом информационную систему.
1.ИНТЕРФЕЙС
Рассмотрим подробнее с помощью каких методов происходит представление информации в “электронный вид”. Говоря об этом, нельзя не сказать о таких понятиях как метафора и интерфейс.
Для начала об интерфейсе. Интерфейс (от англ. interface — поверхность раздела, перегородка) — совокупность средств, методов и правил взаимодействия между элементами системы. Первые интерфейсы появились давно, большая их часть была направлена на узкую среду использования и на довольно узкую группу людей. Например, команды редактирования текста включали в себя указание операции, номер исходной строки, а иногда и номер символа в строке, и (если необходимо) новый текст замены или вставки. Такой интерфейс, хотя и требовал от пользователя удерживать в голове постоянно изменяемое состояние текста, но, несмотря на случавшиеся казусы с сохранением этих изменений, был вполне приемлем для профессионалов. К сожалению, мы видим, что разработчик интерфейса направлял свой продукт на группу людей сугубо связанных с конкретной сферой. Это происходит и сейчас, то есть в действительности конечный пользователь может просто не разобраться в данном продукте. Важно отметить, что хотелось бы, чтобы интерфейс был понятен для всех пользователей: как для новичков, так и для профессионалов. Во избежании этого можно повысить уровень когнитивной нагрузки, но это в свою очередь может привести к увеличению времени работы и к уменьшению эффективности. Соответственно нам нужен интерфейс, который будет интуитивно понятен для пользователей и не будет перегружен. Проблема эта регулярно обсуждается в литературе, однако, как правило, на “интуитивном” уровне, без чёткого видения её сути. Приведём одно из немногих внятных определений интуитивности использования технической системы. Система имеет интуитивно понятный интерфейс (интуитивно пригодна к использованию или пригодна к использованию на интуитивном уровне), если неосознанное применение пользователем уже имеющихся у него знаний приводит к эффективному взаимодействию с нею. Это можно реализовать путём многоуровневой информации, то есть пользователь сам будет входе выполнения нужных ему действий определять свой уровень осведомлённости и попадать на приемлемый ему уровень использования интерфейса. К примеру, ту информацию, которая для опытного пользователя будет очевидной, можно скрыть, тем самым на экране останется только общая информация, отображающая картину в целом. Но для новичка можно будет в любой момент отобразить скрытую информацию, которая бы отображала аспекты работы, ее смысл, какие-нибудь определения, разнообразные изображения, подсказывающие дальнейшие действия. Все это делает интерфейс программы более эффективными и интуитивно-понятным в работе.
2.МЕТАФОРА
С расширением и сферы применения и круга пользователей, при развитии средств ввода и вывода информации возникло качественно новое отношение к новой технике, появилось понимание того, что при взаимодействии с компьютером создаётся некий новый мир. При развитии компьютеризации появилась необходимость в том, что помогает осознать принципиально новые явления, осмыслить происходящее, “очеловечить” компьютерный мир. Понадобился поиск понятий, помогающих каким-то образом уяснить и структурировать неизвестную деятельность. Как мы знаем, это и есть метафоры, представление новых или достаточно необычных для пользователя явлений посредством других более понятных явлений.
Существует иерархия компьютерных метафор. Эта иерархия включает в себя глобальные метафоры проектирования, основные метафоры визуализации и интерфейса и локальные метафоры. Рассмотрим поближе метафоры визуализации и интерфейса.
2.1.МЕТАФОРА ИНТЕРФЕЙСА
Начнём с метафоры интерфейса. Внедрение массовых персональных ЭВМ просто не могло состояться без появления визуальных средств взаимодействия, новых устройств и принципиально новых концепций организации интерфейса с пользователем, например, таких, как концепция “непосредственного действия” (direct manipulation). Концепцию “непосредственного действия” предложил в начале восьмидесятых годов известный специалист в области вычислительных наук профессор Б. Шнейдерман (B. Shneiderman), собрав воедино и проанализировав новые тенденции в организации интерфейса. В настоящее время эта концепция господствует при проектировании интерфейса. Б. Шнейдерман определил следующие характеристики интерфейса, созданного на основе концепции “непосредственного действия”:
1) Постоянное отображение объекта, представляющего интерес;
2) Физические действия (работа с мышью, джойстиком, сенсорным экраном - тач скрином, etc.) или использование функциональной клавиатуры вместо команд со сложным синтаксисом;
3) Быстрые, разбиваемые на шаги, допускающие возврат операции, чьё воздействие на объект, представляющий интерес, немедленно становится видимым;
Суть этого подхода к созданию интерфейса состоит в создании у пользователя впечатления о том, что он непосредственного воздействует на представленные на экране объекты, а не ведёт с ЭВМ диалог об этих объектах. Вместо использования командного языка для описания операций над объектами, пользователь манипулирует видимыми представлениями этих объектов на экране дисплея. Очевидно, что визуальный интерфейс, построенный на этих принципах, немыслим без систематического использования элементов диалога, без некоторого языка, основанного на той или иной системе знаков. Человеко-компьютерное взаимодействие в этой связи может быть чётко описано как знаковый процесс. Метафора интерфейса рассматривается как базовая идея сближения и аналогии между модельными объектами прикладной области и интерактивными объектами. Роль метафоры интерфейса заключается в том, что она способствует лучшему пониманию семантики взаимодействия, а также обеспечивает визуальное представление диалоговых объектов и определяет набор манипуляций пользователя с ними. Метафора интерфейса в этой связи представляется основой знаковой системы, которая в свою очередь лежит в основе диалогового языка. С помощью этого языка пользователь формулирует свою задачу и добивается от компьютера её решения.
Метафоры интерфейса можно подразделить на:
- метафоры, связанные с деятельностью пользователя в той или иной компьютерной среде,
- метафоры режима взаимодействия человека и компьютера,
- метафоры прикладной области.
Метафора интерфейса не только помогает описать абстракции, структурирует понимание новой прикладной области, но и задает объекты диалогового [визуального] языка. Анализ и изучение метафоры интерфейса требует привлечения понятийного аппарата семиотики.
2.2.МЕТАФОРА ВИЗУАЛИЗАЦИИ
Визуализация (хотя и с некоторой натяжкой) может рассматриваться, как особый случай человеко-компьютерного взаимодействия. Следовательно, и метафора визуализации может изучаться, как частный случай метафоры интерфейса.
Более продуктивным является рассмотрение конкретной визуализации как знаковой системы. Здесь в наличии есть набор сущностей визуализируемой модели и набор соответствующих им визуальных образов. Анализ отношений между модельными сущностями позволяет выстроить достаточно чёткие отношения между отдельными визуальными образами, в частности, при разработке конкретного примера визуализации. Вместе с тем, при проектировании полноценной специализированной системы визуализации мало описания знакового соответствия. Нас в этом случае интересует прагматика системы, а также правила представления визуальных образов. Необходимо сделать следующий шаг и выделить язык визуализации в полном семиотическом смысле этого термина.
В качестве грамматики можно рассматривались правила образования конкретных выводов, задающие последовательность смены изображений. В результате язык визуализации разворачивается как набор видов отображения, связанных с сущностями прикладной области.
Разработка теории языка визуализации в свою очередь приводит к необходимости рассмотрения метафоры визуализации, как основной идеи сближения понятий прикладной области с той или иной образностью. Именно метафоры визуализации лежат в основе видов отображения, проектирование которых, в свою очередь, составляет базу проектирования когнитивной составляющей конкретной специализированной системы визуализации.
Пользователь системы имеет дело с конкретными графическими выводами, которые получаются, когда виды отображения “наполняются” реальными данными. Графические выводы являются воплощением абстрактного понятия вида отображения. Смена значимых и значащих картинок (графических выводов) при возможном взаимодействии с изображением является внешней стороной визуализации. Задача проектировщика - разработать набор видов отображения, обладающий такими свойствами, как системность и иерархичность. Идея сближения понятий, лежащая в их основе (метафора), может не быть явно описана в процессе проектирования, но, так или иначе, присутствует в сознании проектировщика. Аналогично, проектировщик может не фиксироваться на том, что данный набор сменяющихся видов отображения вместе с манипуляциями визуальными объектами, реализованный в той или иной визуальной среде, является полноценным языком визуализации. Следует понимать, что зачастую такие абстракции, как метафора и язык визуализации (и даже вид отображения), могут оставаться вне сферы внимания и разработчиков, и пользователей конкретной системы, но, тем не менее, они реально влияют на их деятельность и определяют её суть.
Имеет место ряд связанных между собой базовых понятий, который включает в себя метафору визуализации, язык визуализации, вид отображения, конкретный графический вывод.
Итак, метафора визуализации является системой сближений и аналогий, которая ставится в соответствие понятиям и объектам моделируемой прикладной области. Вторая функция метафоры визуализации - порождение некоторого изобразительного ряда (набора видов отображения) и набора методов взаимодействия с визуальными объектами.
Целью метафоры визуализации является создание наглядных зрительных образов, манипуляции с которыми как-то соответствуют операциям над модельными объектами. Составляющими метафоры визуализации являются ее образность и предписываемые ею действия, как по изменению визуальных образов, так и по манипуляциям пользователей с визуальными объектами. Метафора задаёт контекст, помогающий правильной интерпретации элементов данного языка визуализации, выявлению значения визуального текста.
Таким образом, метафора визуализации обеспечивает понимание отображаемых сущностей прикладной области, а также участвует в создании новых сущностей на базе внутренней логики самой метафоры.
Приведём примеры. Список примеров включает известные метафоры комнаты; здания; города; ландшафта. Распространены метафоры театра (вариант - кукольный театр); книги; комикса; кинематографа. Потенциально возможны варианты - метафора на базе японских комиксов манга и сочетание японских комиксов и мультипликации-аниме – метафора манга+аниме. Посмотрим на эти примеры чуть ближе и увидим, что у каждой распространённой метафоры помимо плюсов есть свои минусы: у метафоры книги плохо сделан поиск нужного текста, и при желании вернуться назад теряется целостность текста, это происходит из-за перехода “один в один”; метафора стены справляется с проблемой поиска, но не может избежать проблемы целостности текста и т.д. Больший интерес у нас вызывает метафора на базе манга+аниме.
2.3.МЕТАФОРА МАНГА+АНИМЕ
Манга - японские комиксы, иногда называемые комику. Аниме – это японская анимация, выполненная по определенным правилам. В Японии мангу читают люди всех возрастов, она уважаема и как форма изобразительного искусства, и как литературное явление, поэтому существует множество произведений самых разных жанров и на самые разнообразные темы: приключения, романтика, спорт, история, юмор, научная фантастика, ужасы, эротика, бизнес и другие. Она стала популярной и в остальном мире, особенно в США, где продажи по данным на 2006 год находились в районе 175—200 млн. долларов. На сегодняшний день манга распространённый и достаточно модный комикс во всём мире. Положение аниме несколько более скромно, но всё же лучшие анимационные фильмы обгоняют по кассовым сборам не только национальное кино, но и голливудские хиты. Из этого всего видно, что аниме и манга имеют огромную популярность во всём мире.
Конечно же нельзя не заметить как хорошо, подробно и чётко происходит передача информации в манга (последовательность картинок), а в аниме – тем более (последовательность видео).
3.ПОСТАНОВКА ЗАДАЧИ
Создать инструктивную с многоуровневым интерфейсом информационную систему, которая будет базироваться на метафоре «манга+аниме». Наша система будет описывать инструкцию по обслуживанию домашнего компьютера.
3.1.ОПИСАНИЕ ЗАДАЧИ
Подчеркну, что данную задачу ещё никто не реализовывал.
Что может быть лучше любой информационной системы? Это инструктор. Секрет этого заключается в том, что инструктор напрямую взаимодействует с клиентом, может ему показать, как делается любое действие, объяснить, зачем это нужно и почему. Поэтому мы будем стремиться сделать такую систему, которая бы учила пользователя «не рассказом, а показом». Задача наглядности является одной из основных идей проекта.
Выбор базовой метафоры «манга+аниме» далеко не случайный! Данная метафора очень хорошо передаёт информацию пользователю, то есть учение происходит, как мы уже говорили «не рассказом, а показом». Не исключено, что мы не будем использовать элементы других метафор.
Интерфейс нашей системы строится как многоуровневый. Он будет иметь два уровня: описание текстом, описание картинками и видео. Текст будет описывать нужные нам действия. Определения, которые могут быть незнакомыми пользователю, нужно объяснить. Самым удобным способом будет показ внешнего вида этой детали и текстовое описание его предназначения. Для реализации сказанного поставим рядом с незнакомым словом свернутый текст в виде «+», при нажатии на него будет развернута область около этого слова, где и будет присутствовать описательная информация, не перекрывая основной текст. Таким образом, пользователь будет знать, как выглядит и зачем нужна любая деталь. Ниже основного теста будут располагаться картинки. В одну отдельную картинку можно заложить основную мысль какого-либо конкретного действия, изобразив лишь его основную идею. Последовательность картинок позволяет показать пользователю последовательность действий, которые ему нужно совершить, чтобы прийти к какому-то определенному результату. Таким образом, в последовательности картинок будут отображены основные идеи действий, и, как следствие, будет показана наглядность происходящего. Присоединение к картинкам видео улучшает понимание информации пользователем. Таким образом, прослеживается определенная связь между манга и аниме.
Несмотря на всё интерфейс будет очень прост и соответственно интуитивно понятен.
Для реализации всего проекта мы предлагаем следующий подход. Работа будет осуществляться в два этапа. Первый – разработка сценария для генерации нужных нам файлов - текстов, картинок, видео. Второй – разработка методов их реализации. Первую часть буду выполнять я. Вторую часть будет выполнять мой коллега, Кожевин Александр. Моя задача заключается в следующем: представить и написать сценарий как программу, генерирующую наборы файлов системы по сценарию, и выдать нужные нам файлы, которые в свою очередь будут использоваться в работе Александра. В результате будет web-страница, описывающая нужную информацию.
4.СЦЕНАРИЙ
Наша система инструктивная. Цель проекта - создание визуального руководства по обслуживанию персонального компьютера. В соответствии с этим система содержит некоторую последовательность действий с их описаниями (текстовые файлы, картинки, видео). Действия последовательности будут зависеть друг от друга. Например, как в любом случае, для выполнения желаемого действия требуется выполнить ещё ряд действий, которые в свою очередь требуют также выполнения каких-либо действий. Отталкиваясь от этого, появляется проблема генерации выводимых данных, описывающих нужную нам информацию. Имея множество описывающих файлов, нам нужно выбрать набор тех, которые нужны нам, учитывая, что должна правильно выполняться зависимость (действие от действия). Для решения этой задачи создадим сценарий генерации нужных нам файлов.
Что такое сценарий? Само понятие сценария возникло вместе с появлением кино и телефильма. Сценарий - литературное произведение, написанное как основа для постановки кино или телефильма. Сценарий в кинематографе, как правило, напоминает пьесу и подробно описывает каждую сцену и диалоги персонажей. Отталкиваясь от этого понятия, проведём аналогию с нашим проектом. Наш результат – информационная система. Также и в киноиндустрии, конечным результатом является “фильм”. Разумеется, сценарий описывает каждую сцену и порядок сцен. Соответственно для нас - это порядок вывода информации (что за чем идёт) и её описание. Можно сделать вывод, что сценарий – текст, описывающий идею генерации данных (файлов) информационной системы, с правильной организацией вывода нужной информации.
4.1.СЦЕНАРИЙ-ПРОГРАММА
Теперь мы хотим, как бы совершить переход от сценария к программе. Так как программа будет лишь частью всей информационной системы, мы смело можем определить сценарий как программа, которая автоматизирует некоторую задачу, которую без сценария пользователь делал бы вручную, используя интерфейс системы.
Программа должна автоматически сгенерировать из имеющегося множества файлов нужный нам набор, то есть сделать правильный выбор нужной информации и вывести её. Нельзя не упомянуть об уже существующих языках сценариев. Язык сценариев(англ. scripting language, в русскоязычной литературе принято название язык сценариев) — язык программирования, разработанный для записи сценариев, последовательностей операций, которые пользователь может выполнять на компьютере. Примером таких языков служит Perl. Языки сценариев удобны в следующих случаях:
1. Если нужно обеспечить программируемость без риска дестабилизировать систему. Так как, неправильно написанная программа выведет диагностическое сообщение, а не приведёт систему к краху;
2. Если важен выразительный код. Во-первых, чем сложнее система, тем больше кода приходится писать. Во-вторых, в языке сценариев может быть совсем другая концепция программирования, чем в основной программе. В-третьих, язык сценариев имеет собственный проблемно-ориентированный набор команд, и одна строка программы может делать то же, что несколько десятков строк на традиционном языке.
3. Если требуется, чтобы их исполняемые файлы можно было запускать на различных платформах без предварительной перекомпиляции.
Мы же в свою очередь будем использовать язык программирования C#, так как в нашем случае плюсы языков сценария не важны и будут почти не заметны.
Перейдём к этапу проектирования нашего сценария. Наша система подразумевает, как это уже говорилось, какое-то количество действий. Итак, каждое действие имеет описательную (текстовый файл) и показательную (картинки и видео файлы) части. Также, каждое действие имеет зависимость от ряда других действий. Нужно найти и вывести все части нужного нам действия и все части действий, которые для него нужны. Например, для выполнения действия «2» нужно выполнить действия «1» и «3», при этом действие «2» имеет описательную часть в виде текстового файла и показательную часть в виде четырёх картинок и одного видео, в свою очередь действие «1» имеет также текстовый файл, три картинки и одно видео, а действие «3» имеет текстовый файл, шесть картинок и два видео. Наша программа-сценарий должна сгенерировать набор файлов, состоящий из трёх текстовых файлов, тринадцати картинок и четырёх видео файлов.
Очевиден вопрос – как правильно реализовать зависимость наших действий друг от друга, учитывая, что зависимости могут быть очень «глубокими», то есть действие зависит от действий, которые в свою очередь так же зависят от действий, которые соответственно зависят от каких-то других действий и т.д. Как решение этой проблемы мы предлагаем модель, основанную на хорошо известном нам графе.
4.1.СЦЕНАРИЙ КАК ГРАФ
Граф — это совокупность непустого множества вершин и множества пар вершин (рёбер). Очевидно, что множество вершин графа будет множеством наших действий, а зависимости между ними будут рёбра графа. Таким образом, мы сможем наиболее точно передать зависимость действий между собой, насколько «глубока» она бы не была. К каждой вершине графа сделаем привязку на тексты, картинки и видео. Граф будет ориентированным для уточнения зависимостей. Таким образом, мы полностью описали наше множество действий и все их зависимости. Помимо этого, граф очень понятен в плане представления. В дальнейшем можно осуществить программирование по образцам, где действия будут сводиться к построению графа.
Рассмотрим пример.
Действие (вершина) «1» не зависит ни от одного действия; действие «2» зависит от действия «1»; действие «3» не зависит ни от одного действия; действие «4» зависит от действия «1»; действие «5» зависит от действий «1», «3» и «4»; действие «6» зависит от действий «1», «2» и «3».
Конечно же, надо определить, как будет осуществляться поиск всех зависимых действий по заданному действию. В этом нам поможет поиск в глубину. Поиск в глубину - один из методов обхода графа. Алгоритм поиска описывается следующим образом: для каждой не пройденной вершины необходимо найти все не пройденные смежные вершины и повторить поиск для них. Таким образом, с помощью поиска в глубину, мы, начиная алгоритм от заданной вершины, обойдём все вершины (действия), от которых будет зависеть наше действие. Стоит отметить, что при задании зависимостей некоторые из них можно не задавать, примером тому будут зависимости между действиями «5» и «1», «6» и «1» (возвращаясь к нашему примеру). Это происходит из-за того, что действие «5» зависит от действия «4», которое в свою очередь зависит от действия «1». То есть поиск в глубину в любом случае дойдёт до действия «1». Соответственно возникает вопрос – удобно ли это будет при задании зависимостей? Переформулируем вопрос - хочется ли нам продумывать с самого начала все зависимости? Нет, так как вся наша система может содержать очень много действий, а это приведёт к большой затрате сил, да и просто проще.
Раз уж мы коснулись вопроса задания зависимостей между действиями, надо чётко определиться, как, и в какой форме, они будет задаваться для программы. Зависимости будем задавать, аналогично с графом, следующим образом: в текстовом файле, с именем actions.txt, самая верхняя строка будет содержать количество действий; первая строка после верхней будет содержать номера действий, от которых будет зависеть первое действие; вторая строка после верхней будет содержать номера действий, от которых будет зависеть второе действие; и т.д. Ясно, что если количество всех действий системы будет N, то количество строк в файле actions.txt будет N+1. Рассмотрим пример, описывающий ранее приведённый пример.
Теперь поговорим об организации нашего множества данных. Создадим три каталога: картинки, видео и текстовые файлы. Для удобства, в качестве имен файлов будут номера соответствующих действий.
Работоспособность программы будет зависеть от правильности введения данных. Если мы не правильно ввели зависимости действий, то в нашем графе может образоваться цикл. Тем самым программа не будет работать. В этом случае нам необходимо реализовать проверку на цикличность – и в случае цикла будем сообщать об этом. Также сделаем и со случаем, когда может не хватать каких-то файлов.
Теперь поговорим о выводе данных. Вывод данных будет зависеть от нашего запроса действий. Мы можем ввести как одно, так и несколько действий. Например, на первом рисунке запрос на действие «7», на втором на действия «7» и «8».
Вывод данных будет находиться в текстовом файле: .
Через пустую строку будет начинаться следующее запрошенное действие.
4.3.ПРОГРАММИРОВАНИЕ СЦЕНАРИЯ ПУТЁМ ДЕМОНСТРАЦИЙ
Термин "Программирование по образцам", определяется для систем, которые позволяют программисту использовать образцы вводимой и выводимой информации в течение процесса программирования. В программировании по образцам можно выделить два основных аспекта - формирование образцов (примеров) ввода и вывода и процесс логического вывода (или угадывания) программы по этим образцам.
Программирование путём демонстраций ведётся посредством манипуляций над данными на экране, демонстрирующими вычислительной системе, что программа должна делать. Преимущества такого подхода очевидны - программисту легче выполнить нечто, чем описать это текстуально. Как пишет Сайфер - "Доводы за программирование путём демонстраций просты и неотразимы - если пользователь знает, как решить задачу на ЭВМ, то он должен быть способен создать программу для решения этой задачи. И вовсе не обязательно учить язык программирования типа Си или Бейсика. Вместо этого пользователь должен иметь возможность проинструктировать ЭВМ: "наблюдай за тем, что я делаю", и ЭВМ создаст программу, соответствующую действиям пользователя.
Несмотря на то, что наша главная задача выполнена, сделаем редактирование и создание сценария путём программирования по образцам. Как мы уже говорили, наш сценарий представлен виде графа, значит, редактировать и создавать нам нужно граф. Напишем программу на языке C# для редактирования графа по образцам. Результат программы будет файл actions.txt, который в свою очередь будет использовать наша программа сценария, соответственно он будет организован точно так же, как и описывалось ранее. Диалоговое окно программы имеет следующий вид.
При нажатии вкладки ОТКРЫТЬ, открывается и отображается граф, представленный в файле (файл должен иметь такое же строение, как и файл actions.txt). При нажатии вкладки СОХРАНИТЬ нужно, указать имя сохраняемого файла, в результате создаётся текстовый файл с описанием графа. При нажатии вкладки СОЗДАТЬ требуется ввести количество вершин графа. Рассмотрим пример создания и редактирования графа с восьмью вершинами. Вершины графа пронумерованы и расположены на окружности.
Если кликнуть на вершину, то она окраситься жёлтым цветом, это значит что вершина выделена, затем, кликнем на другую вершину, получится ребро графа, направленное из жёлтой вершину в указанную. Таким образом, мы можем легко редактировать и задавать нужную нам зависимость действий.
ЗАКЛЮЧЕНИЕ
В ходе работы была получена реализация информационной системы на примере мануала по обслуживанию персонального компьютера. Система имеет собственный сценарий, который представляет собой программу, написанную на языке программирования C#. Редактирование сценария улучшено путём программирования по образцам, что значительно повысило и упростило применение и использование данного сценария для других информационных систем.
Данная система может быть реализована на любом другом примере. Система очень удобна в применении, скорость её использования высока, так как она имеет многоуровневый и интуитивно понятный интерфейс. Данную систему можно применять в любом обучающем и объясняющем материале.
Пользователь системы работает на страницах сайта.
При выборе нужного действия он получает описательную информацию.
Для получения неизвестных ему терминов, действий, пользователь может узнать об этом, развернув частично или полностью страницу.
Полностью развёрнутая страница будет выглядеть так.
ЛИТЕРАТУРА
Авербух В.Л., Байдалин А.Ю., Казанцев А.Ю., Рябинина Л.Б. Метафоры и комплексные виды отображения для систем информационной визуализации // Пробл. Теорет. и прикл. Математики: Тр.36 Регион. Мол. Конф. Екатеринбург, ИММ Уро РАН, 2005. С.314-315.
Авербух В.Л. К теории компьютерной визуализации // Вычислительные технологии Т. 10, N 4, 2005, стр 21-51.
Watch What I Do/ Programming by Demonstration. (Ed.- Allen Cypher) MIT Press. Cambridge, (Mass.), 1993.
Averbukh V.L., Bakhterev M.O., Baydalin A.Yu., Gorbashevskiy D.Yu., Ismagilov D.R., Kazantsev A.Yu., Nebogatikova P.V., Popova A.V., Vasev P.A. Searching and Analysis of Interface and Visualization Metaphors // Human – computer Interaction? New Developments. / Edited by Kikuo Asai. Chapter 3, Vienna, In-teh. ISBN 978-953-7619-15-5, pp. 49-84.
Averbukh K.L. Magic Fairy Tales as Source for Interface Metaphors // Journal of // HCI Vistas Vol. IV, UX Design / Article 9.
ПРИЛОЖЕНИЕ
Код программы сценария.
namespace Boris
{
public class Constants
{
public const string textDir = @"Files\Actions";
public const string imageDir = @"Files\Photos";
public const string videoDir = @"Files\Videos";
}
}
using System;
namespace Boris
{
public class CycleException : Exception
{}
}
using System.Collections.Generic;
namespace Boris
{
public interface IPrinter
{
void Print(IEnumerable<int> needActins);
}
}
namespace Boris
{
public interface IGraph
{
int[] GetPreviousVertexes(int num);
}
}
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Boris
{
public class Printer : IPrinter
{
private readonly TextWriter writer;
private string[] imageFiles;
private string[] textFiles;
private string[] videoFiles;
public Printer(TextWriter writer)
{
this.writer = writer;
imageFiles = Directory.GetFiles(Constants.imageDir);
textFiles = Directory.GetFiles(Constants.textDir);
videoFiles = Directory.GetFiles(Constants.videoDir);
}
private static string[] GetFiles(IEnumerable<string> files, int num)
{
return files.Where(x => Path.GetFileNameWithoutExtension(x).Split('-')[0].Equals("" + num)).ToArray();
}
public void Print(IEnumerable<int> needActins)
{
foreach (var i in needActins)
{
writer.WriteLine("Action: " + (i + 1));
writer.WriteLine(" TextFiles: " + (GetFiles(textFiles, i + 1).Length > 0?"":"files not found"));
foreach (var i1 in GetFiles(textFiles, i + 1))
{
writer.WriteLine(" " + i1);
}
writer.WriteLine(" ImageFiles: " + (GetFiles(imageFiles, i + 1).Length > 0 ? "" : "files not found"));
foreach (var i1 in GetFiles(imageFiles, i + 1))
{
writer.WriteLine(" " + i1);
}
writer.WriteLine(" VideoFiles: " + (GetFiles(videoFiles, i + 1).Length > 0 ? "" : "files not found"));
foreach (var i1 in GetFiles(videoFiles, i + 1))
{
writer.WriteLine(" " + i1);
}
}
writer.WriteLine("");
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Boris
{
public class Graph : IGraph
{
private int[][] graph;
private int n;
private List<int> order;
public Graph(TextReader reader)
{
n = Int32.Parse(reader.ReadLine());
graph = new int[n][];
for (int i = 0; i < n; ++i)
{
graph[i] = new int[n];
string[] strings = reader.ReadLine().Split(' ').Where(x => !string.IsNullOrEmpty(x)).ToArray();
var s = strings.Select(x => Int32.Parse(x) - 1).ToArray();
for (int j = 0; j < s.Length; ++j)
{
graph[i][s[j]] = 1;
}
}
order = new List<int>();
Sort();
}
private void Sort()
{
var set = new HashSet<int>();
for (int i = 0; i < n; ++i )
Sort(i, set);
Check();
}
private void Sort(int i, HashSet<int> set)
{
if (!set.Contains(i))
{
set.Add(i);
for (int j = 0; j < n; ++j )
{
if (graph[i][j] != 0 && !set.Contains(j))
Sort(j, set);
}
order.Add(i);
}
}
private void Check()
{
for (int i = 0; i < n; ++i)
{
for (int j =0; j < i; ++j)
{
if (graph[order[j]][order[i]] == 1)
throw new CycleException();
}
}
}
public int[] GetPreviousVertexes(int num)
{
int[] colors = new int[n];
Stack<int> stack = new Stack<int>();
stack.Push(num);
colors[num] = 1;
var result = new List<int>();
result.Add(num);
while (stack.Count > 0)
{
int v = stack.Pop();
for (int i = 0; i < n; ++i)
{
if (graph[v][i] == 1 && colors[i] == 0)
{
colors[i] = 1;
stack.Push(i);
result.Add(i);
}
}
}
result = result.OrderBy(x => order.IndexOf(x)).ToList();
return result.ToArray();
}
}
}
using System;
using System.IO;
namespace Boris
{
internal class Program
{
private static void Main(string[] args)
{
var reader = new StreamReader("actions.txt");
var output = new StreamWriter("out.txt");
try
{
IGraph graph = new Graph(reader);
var input = new StreamReader("in.txt");
IPrinter printer = new Printer(output);
while (!input.EndOfStream)
{
int curAct = Int32.Parse(input.ReadLine()) - 1;
printer.Print(graph.GetPreviousVertexes(curAct));
}
} catch (CycleException e)
{
output.WriteLine("Cycle was founded in input graph");
}
output.Close();
}
}
}
Код программы редактирования сценария
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using ColoringGraphProblem.Views;
namespace ColoringGraphProblem
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new GraphCreatingForm());
}
}
}
using ColoringGraphProblem.Exceptions;
namespace ColoringGraphProblem
{
//Вершины нумеруются с 0
public class Graph
{
//количество вершин - брать значение можем хоть откуда; присваивать только изнутри
public int VertexCount { get; private set; }
//матрица смежности графа
private readonly bool[,] adjacencyMatrix;
//конструктор. создает матрицу графа и проверяет, что количество вершин положительно
public Graph(int vertexCount)
{
if (vertexCount <= 0)
throw new UserException("Количество вершин в графе должно быть положительным числом. Значение {0} неприемлемо.", vertexCount);
VertexCount = vertexCount;
adjacencyMatrix = new bool[vertexCount, vertexCount];
}
//Добавить ребро
public void AddEdge(int v1, int v2)
{
adjacencyMatrix[v1, v2] = true;
}
//Удалить ребро
public void DeleteEdge(int v1, int v2)
{
adjacencyMatrix[v1, v2] = false;
}
//Узнать есть ли ребро
public bool HasEdge(int v1, int v2)
{
return adjacencyMatrix[v1, v2];
}
}
}
using System.IO;
using System.Linq;
using System.Threading;
using ColoringGraphProblem.Exceptions;
namespace ColoringGraphProblem.WorkWithFiles
{
public class GraphReader
{
//Прочитать граф из файла по пути filePath
public static Graph Read(string filePath, int maxGraphSize)
{
string[] text = File.ReadAllLines(filePath).ToArray();
if (text.Length == 0)throw new UserException("Ожидается, что файл не пустой");
int vertexAmount = int.Parse(text[0]);
if (vertexAmount > maxGraphSize)
throw new UserException("Ожидается, что количество вершин в графе будет не больше, чем {0}. Значение {1} неприемлемо.", maxGraphSize, vertexAmount);
var result = new Graph(vertexAmount);
for (int i = 1; i <= vertexAmount; i++)
{
var args = text[i].Split(' ').Where(str => !string.IsNullOrEmpty(str));
foreach(var arg in args)
{
int val = int.Parse(arg);
if (val < 1 || val > vertexAmount) throw new UserException("Номера вершин должны быть от 1 до {0}", vertexAmount);
if (val == i) throw new UserException("В графе не должно быть петель");
if (result.HasEdge(i-1, val-1)) throw new UserException("В графе не должно быть мультиребер.");
result.AddEdge(i-1, val-1);
}
}
return result;
}
}
}
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace ColoringGraphProblem.WorkWithFiles
{
public class GraphWriter
{
//записывает граф graph в файл по пути filePath
public static void Write(string filePath, Graph graph)
{
var sb = new StringBuilder();
sb.AppendLine(string.Format("{0}", graph.VertexCount));
for (int i = 0; i < graph.VertexCount; i++)
{
for (int j = 0; j < graph.VertexCount; j++)
if (graph.HasEdge(i, j))
sb.Append(string.Format("{0} ", j+1));
sb.AppendLine();
}
File.WriteAllText(filePath, sb.ToString());
}
}
}
using System;
namespace ColoringGraphProblem.Exceptions
{
public class InnerException : Exception
{
public InnerException(string text, params object[] args)
: base(string.Format(text, args))
{
}
}
}
using System;
namespace ColoringGraphProblem.Exceptions
{
public class UserException : Exception
{
public UserException(string text, params object[] args)
: base(string.Format(text, args))
{
}
}
}
using System.Drawing;
using System.Windows.Forms;
namespace ColoringGraphProblem.Drawing
{
//Полотно на котором мы будем рисовать
//
// Вся отрисовка идет на PictureBox
// Вся отрисовка идет на Graphics - это то, на чем у PictureBox-а рисуют
public class Canvas
{
public Canvas(PictureBox pictureBox)
: this(pictureBox.CreateGraphics(), pictureBox.Height, pictureBox.Width)
{
}
public Canvas(PictureBox pictureBox, Graphics graphics)
: this(graphics, pictureBox.Height, pictureBox.Width)
{
}
public Canvas(Graphics graphics, int height, int width)
{
Graphics = graphics;
Height = height;
Width = width;
}
//Привести точку из квадрата от 0 до 1 в пиксельные координаты
public Point NormalizePoint(Point point)
{
double xScale = Width;
double yScale = Height;
return new Point(xScale * point.X, yScale * point.Y);
}
public Graphics Graphics { get; private set; }
public int Height { get; private set; }
public int Width { get; private set; }
}
}
using System;
using System.Drawing;
namespace ColoringGraphProblem.Drawing
{
//Класс для перевода раскраски алгоритма в случайные цвета Windows
public class Colorer
{
//readonly - можем присваивать значение только в конструкторе
private readonly Color[] color;
//neededColors - количество случайных цветов Windows которые нам нужны для раскраски
public Colorer(int neededColors)
{
var random = new Random(146);
color = new Color[neededColors];
for (int i = 0; i < neededColors; i++)
color[i] = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256));
}
public Color[] CreateColoring(int[] coloring)
{
var result = new Color[coloring.Length];
for (int i = 0; i < coloring.Length; i++)
result[i] = color[coloring[i]-1];
return result;
}
}
}
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ColoringGraphProblem.Drawing
{
//Класс, который отлавливает событие клика по графу и что-то отмечает
public class GraphClicker
{
//максимальное расстояние от центра вершины графа при котором считается, что мы кликнули по ней
private const double MaximalClickableDist = 20;
public PictureBox PictureBox { get; private set; }
private readonly GraphDrawer drawer;
public GraphClicker(PictureBox pictureBox)
{
PictureBox = pictureBox;
drawer = new GraphDrawer();
//подписываемся на событие клика PictureBox.Click
PictureBox.Click += MouseLeftButtonClick;
}
public Graph Graph { get; private set; }
public bool[] clicked;
//Отрисовка графа с выделением кликнутой вершины
public void Draw(Canvas canvas)
{
//Подготовили массив системных цветов для отрисовки графа
var coloring = new Color[Graph.VertexCount];
for (int i = 0; i < Graph.VertexCount; i++)
if (clicked[i])
coloring[i] = GraphDrawingConstants.GraphSelectedVertexBrushColor;
else
coloring[i] = GraphDrawingConstants.GraphVertexBrushColor;
drawer.Draw(canvas, Graph, coloring);
}
private void ClickOnVertex(Canvas canvas, int vertexId)
{
//Если вершина уже была выделена, то снимаем выделение
if (clicked[vertexId])
clicked[vertexId] = false;
//Если вершина не была выделена, то
else
{
//пытаемся найти уже выделенную вершину
int vertex1 = -1;
for (int i = 0; i < clicked.Length; i++)
if (clicked[i])
vertex1 = i;
//если уже была выделенная вершина
if (vertex1 != -1)
{
int vertex2 = vertexId;
if (Graph.HasEdge(vertex1, vertex2))
Graph.DeleteEdge(vertex1, vertex2);
else
Graph.AddEdge(vertex1, vertex2);
clicked[vertex1] = false;
}
else
clicked[vertexId] = true;
}
//перерисовываем граф
Draw(canvas);
}
//Координаты центра вершины vertexId из vertexCount в пикселях
private static Point GetVertexCenterInPixels(Canvas canvas, int vertexId, int vertexCount)
{
return canvas.NormalizePoint(GraphDrawer.GetVertexCenter(vertexId, vertexCount));
}
private void ProcessClick(int x, int y)
{
//если нет графа, то ничего не делаем
if (Graph == null) return;
var canvas = new Canvas(PictureBox);
var cur = new Point(x, y);
//находим самый близкий к cur центр вершины
var bestDist = cur.Dist(GetVertexCenterInPixels(canvas, 0, Graph.VertexCount)); ;
int bestVertexId = 0;
for (int i = 1; i < Graph.VertexCount; i++)
if (bestDist > cur.Dist(GetVertexCenterInPixels(canvas, i, Graph.VertexCount)))
{
bestDist = cur.Dist(GetVertexCenterInPixels(canvas, i, Graph.VertexCount));
bestVertexId = i;
}
//Если расстояние меньше либо равно, чем MaximalClickableDist
if (bestDist <= MaximalClickableDist)
ClickOnVertex(canvas, bestVertexId);
}
//метод, который вызовется по клику мыши
private void MouseLeftButtonClick(object sender, EventArgs e)
{
//если событие для мыши
if (e is MouseEventArgs)
{
var me = (MouseEventArgs)e;
//если событие клика левой кнопки мыши
if (me.Button == MouseButtons.Left)
{
ProcessClick(me.X, me.Y);
}
}
}
public bool HasGraph()
{
return Graph != null;
}
public void SetGraph(Graph graph)
{
Graph = graph;
clicked = new bool[Graph.VertexCount];
}
}
using System;
using System.Drawing;
using ColoringGraphProblem.Exceptions;
namespace ColoringGraphProblem.Drawing
{
//рисует граф
public class GraphDrawer
{
//радиус круга графа
private const double graphRadius = 0.4;
//радиус вершины
private const double vertexRaduis = 0.02;
private readonly PrimitiveDrawer primitiveDrawer;
public GraphDrawer()
{
primitiveDrawer = new PrimitiveDrawer();
}
//нарисовать граф c цветами вершин по умолчанию
public void Draw(Canvas canvas, Graph graph)
{
var coloring = new Color[graph.VertexCount];
for (int i = 0; i < coloring.Length; i++)
coloring[i] = GraphDrawingConstants.GraphVertexBrushColor;
Draw(canvas, graph, coloring);
}
//нарисовать граф с цветами вершин coloring
public void Draw(Canvas canvas, Graph graph, Color[] coloring)
{
if (coloring.Length != graph.VertexCount)
throw new InnerException("Размерность {0} окраски не соотвествует количеству {1} вершин графа.", coloring.Length, graph.VertexCount);
primitiveDrawer.ClearCanvas(canvas);
for (int i = 0; i < graph.VertexCount; i++)
for (int j = 0; j < graph.VertexCount; j++)
if (graph.HasEdge(i, j)) DrawEdge(canvas, i, j, graph.VertexCount);
for (int i = 0; i < graph.VertexCount; i++)
{
DrawVertex(canvas, coloring[i], i, graph.VertexCount);
}
}
//Нарисовать ребро из вершины с номером v1 в вершину с номером v2 из vertexCount
private void DrawEdge(Canvas canvas, int v1, int v2, int vertexCount)
{
primitiveDrawer.DrawLine(canvas, GraphDrawingConstants.GraphVertexPen, GetVertexCenter(v1, vertexCount), GetVertexCenter(v2, vertexCount), vertexRaduis);
}
//Нарисовать вершину с номером id из vertexCount
private void DrawVertex(Canvas canvas, Color color, int id, int vertexCount)
{
primitiveDrawer.DrawCircle(canvas, GraphDrawingConstants.GraphVertexPen, new SolidBrush(color), GetVertexCenter(id, vertexCount), vertexRaduis);
primitiveDrawer.DrawText(canvas, new SolidBrush(GraphDrawingConstants.GraphVertexPenColor), GetVertexCenter(id, vertexCount), (id + 1).ToString(), GraphDrawingConstants.Font);
}
//Центр вершины с номером id из vertexCount в квадрате [0,0,1,1]
public static Point GetVertexCenter(int id, int vertexCount)
{
double angle = (Math.PI*2.0/vertexCount) * id;
return new Point(Math.Cos(angle) * graphRadius + 0.5, Math.Sin(angle) * graphRadius + 0.5);
}
}
}
using System.Drawing;
namespace ColoringGraphProblem.Drawing
{
public static class GraphDrawingConstants
{
//цвет внутренности вершины
public static Color GraphVertexBrushColor = Color.AliceBlue;
//кисть внутренности вершины
public static Brush GraphVertexBrush = new SolidBrush(GraphVertexBrushColor);
//цвет внутренности выделенной вершины
public static Color GraphSelectedVertexBrushColor = Color.Yellow;
//кисть внутренности выделенной вершины
public static Brush GraphSelectedVertexBrush = new SolidBrush(GraphSelectedVertexBrushColor);
//цвет границы вершины
public static Color GraphVertexPenColor = Color.Black;
//ручка границы вершины
public static Pen GraphVertexPen = new Pen(GraphVertexPenColor, 2);
//фоновый цвет
public static Color BackgroundColor = Color.AliceBlue;
//фоновая кисть
public static Brush BackgroundColorBrush = new SolidBrush(BackgroundColor);
//шрифт
public static Font Font = new Font(new FontFamily("Times New Roman"), 12);
}
}
using System;
namespace ColoringGraphProblem.Drawing
{
//Точка
public class Point
{
public Point(double x, double y)
{
X = x;
Y = y;
}
//Расстояние до другой точки
public double Dist(Point p)
{
return Math.Sqrt((X - p.X) * (X - p.X) + (Y - p.Y) * (Y - p.Y));
}
public double X{ get; private set;}
public double Y{ get; private set;}
}
}
using System;
using System.Drawing;
namespace ColoringGraphProblem.Drawing
{
public class PrimitiveDrawer
{
//очистить полотно
public void ClearCanvas(Canvas canvas)
{
canvas.Graphics.FillRectangle(GraphDrawingConstants.BackgroundColorBrush, 0, 0, canvas.Width, canvas.Height);
}
public void DrawText(Canvas canvas, Brush brush, Point center, string text, Font font)
{
Point realCenter = canvas.NormalizePoint(center);
var rect = canvas.Graphics.MeasureString(text, font);
canvas.Graphics.DrawString(text, font, brush, new PointF{X = (float)realCenter.X-rect.Width/2, Y = (float)realCenter.Y-rect.Height/2});
}
//нарисовать круг
public void DrawCircle(Canvas canvas, Pen border, Brush brush, Point center, double radius)
{
double xScale = canvas.Width;
double yScale = canvas.Height;
Point realCenter = canvas.NormalizePoint(center);
double realRadius = radius * Math.Min(xScale, yScale);
canvas.Graphics.FillEllipse(brush,
(int)(realCenter.X - realRadius),
(int)(realCenter.Y - realRadius),
(int)(2.0 * realRadius),
(int)(2.0 * realRadius));
canvas.Graphics.DrawEllipse(border,
(int)(realCenter.X - realRadius),
(int)(realCenter.Y - realRadius),
(int)(2.0 * realRadius),
(int)(2.0 * realRadius));
}
//нарисовать линию
public void DrawLine(Canvas canvas, Pen border, Point p1, Point p2, double radius)
{
const double angle30 = 0.52359877559829887307710723054658;
double xScale = canvas.Width;
double yScale = canvas.Height;
double realRadius = radius * Math.Min(xScale, yScale);
Point realP1 = canvas.NormalizePoint(p1);
Point realP2 = canvas.NormalizePoint(p2);
var x = realP2.X - realP1.X;
var y = realP2.Y - realP1.Y;
double d = Math.Sqrt(x * x + y * y);
x /= d;
y /= d;
var x1 = x * Math.Cos(angle30) - y * Math.Sin(angle30);
var y1 = x * Math.Sin(angle30) + y * Math.Cos(angle30);
var x2 = x * Math.Cos(-angle30) - y * Math.Sin(-angle30);
var y2 = x * Math.Sin(-angle30) + y * Math.Cos(-angle30);
x = x * realRadius;
y = y * realRadius;
realP1 = new Point(realP1.X + x, realP1.Y + y);
realP2 = new Point(realP2.X - x, realP2.Y - y);
canvas.Graphics.DrawLine(border, (int)realP1.X, (int)realP1.Y, (int)realP2.X, (int)realP2.Y);
canvas.Graphics.DrawLine(border, (int)(realP2.X - x1 * realRadius), (int)(realP2.Y - y1 * realRadius), (int)realP2.X, (int)realP2.Y);
canvas.Graphics.DrawLine(border, (int)(realP2.X - x2 * realRadius), (int)(realP2.Y - y2 * realRadius), (int)realP2.X, (int)realP2.Y);
}
}
}
using System.Drawing;
using System.Windows.Forms;
using ColoringGraphProblem.Drawing;
using ColoringGraphProblem.Exceptions;
using ColoringGraphProblem.Views.Dialogs;
using ColoringGraphProblem.WorkWithFiles;
namespace ColoringGraphProblem.Views
{
public class GraphCreatingModel
{
//форма
private readonly GraphCreatingForm form;
//содержит в себе граф
private readonly GraphClicker graphClicker;
//Есть ли граф
public bool HasGraph()
{
return graphClicker.HasGraph();
}
//запомнили форму и создали clicker
public GraphCreatingModel(GraphCreatingForm form)
{
this.form = form;
graphClicker = new GraphClicker(form.GetPictureBox());
}
//создать граф
public void CreateGraph(IWin32Window owner)
{
//выдаем диалог ввода числа
var inputIntegerModel = new InputIntegerModel("Введите число...", "Введите количество вершин в графе:");
var nullableValue = inputIntegerModel.GetValue(owner);
if (nullableValue == null) return;
var value = nullableValue.Value;
if (value <= 0)
throw new UserException("Количество вершин в графе должно быть больше нуля. Значение {0} недопустимо.", value);
if (value > 30)
throw new UserException("Ожидается, что количество вершин в графе будет не больше, чем {0}. Значение {1} неприемлемо.", 30, value);
graphClicker.SetGraph(new Graph(value));
}
//зачитываем граф и кладем его в clicker
public void OpenGraph(string filePath)
{
graphClicker.SetGraph(GraphReader.Read(filePath, 30));
}
//Нарисовать граф, если он есть
public void Draw()
{
if (graphClicker.HasGraph())
graphClicker.Draw(new Canvas(form.GetPictureBox()));
}
//Нарисовать граф, если он есть
public void Draw(Graphics graphics)
{
if (graphClicker.HasGraph())
graphClicker.Draw(new Canvas(form.GetPictureBox(), graphics));
}
//пишем граф в файл
public void SaveGraph(string filePath)
{
GraphWriter.Write(filePath, graphClicker.Graph);
}
}
}
using System;
using System.Windows.Forms;
using ColoringGraphProblem.Exceptions;
using ColoringGraphProblem.Views.CommonForms;
namespace ColoringGraphProblem.Views
{
public partial class GraphCreatingForm : Form
{
private GraphCreatingModel model;
public PictureBox GetPictureBox()
{
return pictureBox;
}
public GraphCreatingForm()
{
InitializeComponent();
}
//Вызывается, когда пользователь в меню выбирает соотвествующую кнопку
private void создатьToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
model.CreateGraph(this);
model.Draw();
}
catch (UserException ue)
{
new ErrorMessageModel().ShowMessage(this, ue.Message);
}
}
//Вызывается, когда пользователь в меню выбирает соотвествующую кнопку
private void открытьToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
if (openFileDialog.ShowDialog(this) == DialogResult.OK)
{
if (openFileDialog.CheckFileExists)
{
model.OpenGraph(openFileDialog.FileName);
model.Draw();
}
else throw new UserException("Файл '{0}' не найден.", openFileDialog.FileName);
}
}
catch (UserException ue)
{
new ErrorMessageModel().ShowMessage(this, ue.Message);
}
}
//Вызывается, когда пользователь в меню выбирает соотвествующую кнопку
private void сохранитьToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!model.HasGraph()) return;
try
{
if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
model.SaveGraph(saveFileDialog.FileName);
}
catch (UserException ue)
{
new ErrorMessageModel().ShowMessage(this, ue.Message);
}
}
//Вызывается, когда пользователь в меню выбирает соотвествующую кнопку
private void выйтиToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
//Вызывается, когда меняется размер формы и PictureBox
private void pictureBox_Resize(object sender, EventArgs e)
{
while (model == null) ;
model.Draw();
}
//Вызывается, когда PictureBox'у хочется перерисоваться
private void pictureBox_Paint(object sender, PaintEventArgs e)
{
while (model == null) ;
model.Draw(e.Graphics);
}
//Вызывается, когда произошла первая отрисовка формы
private void GraphCreatingForm_Shown(object sender, EventArgs e)
{
model = new GraphCreatingModel(this);
}
}
}
namespace ColoringGraphProblem.Views
{
partial class GraphCreatingForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.файлToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.открытьToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.создатьToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.сохранитьToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
this.выйтиToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.pictureBox = new System.Windows.Forms.PictureBox();
this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
this.menuStrip1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox)).BeginInit();
this.SuspendLayout();
//
// menuStrip1
//
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.файлToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(427, 24);
this.menuStrip1.TabIndex = 0;
this.menuStrip1.Text = "menuStrip";
//
// файлToolStripMenuItem
//
this.файлToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.открытьToolStripMenuItem,
this.создатьToolStripMenuItem,
this.сохранитьToolStripMenuItem,
this.toolStripMenuItem1,
this.выйтиToolStripMenuItem});
this.файлToolStripMenuItem.Name = "файлToolStripMenuItem";
this.файлToolStripMenuItem.Size = new System.Drawing.Size(48, 20);
this.файлToolStripMenuItem.Text = "Файл";
//
// открытьToolStripMenuItem
//
this.открытьToolStripMenuItem.Name = "открытьToolStripMenuItem";
this.открытьToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.открытьToolStripMenuItem.Text = "Открыть";
this.открытьToolStripMenuItem.Click += new System.EventHandler(this.открытьToolStripMenuItem_Click);
//
// создатьToolStripMenuItem
//
this.создатьToolStripMenuItem.Name = "создатьToolStripMenuItem";
this.создатьToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.создатьToolStripMenuItem.Text = "Создать";
this.создатьToolStripMenuItem.Click += new System.EventHandler(this.создатьToolStripMenuItem_Click);
//
// сохранитьToolStripMenuItem
//
this.сохранитьToolStripMenuItem.Name = "сохранитьToolStripMenuItem";
this.сохранитьToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.сохранитьToolStripMenuItem.Text = "Сохранить";
this.сохранитьToolStripMenuItem.Click += new System.EventHandler(this.сохранитьToolStripMenuItem_Click);
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(149, 6);
//
// выйтиToolStripMenuItem
//
this.выйтиToolStripMenuItem.Name = "выйтиToolStripMenuItem";
this.выйтиToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.выйтиToolStripMenuItem.Text = "Выйти";
this.выйтиToolStripMenuItem.Click += new System.EventHandler(this.выйтиToolStripMenuItem_Click);
//
// pictureBox
//
this.pictureBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.pictureBox.Location = new System.Drawing.Point(0, 27);
this.pictureBox.Name = "pictureBox";
this.pictureBox.Size = new System.Drawing.Size(427, 311);
this.pictureBox.TabIndex = 1;
this.pictureBox.TabStop = false;
this.pictureBox.Resize += new System.EventHandler(this.pictureBox_Resize);
this.pictureBox.Paint += new System.Windows.Forms.PaintEventHandler(this.pictureBox_Paint);
//
// openFileDialog
//
this.openFileDialog.Filter = "Text files|*.txt|All files|*.*";
//
// saveFileDialog
//
this.saveFileDialog.Filter = "Text files|*.txt|All files|*.*";
//
// GraphCreatingForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(427, 337);
this.Controls.Add(this.pictureBox);
this.Controls.Add(this.menuStrip1);
this.MainMenuStrip = this.menuStrip1;
this.Name = "GraphCreatingForm";
this.Text = "Создание или редактирование графа";
this.Shown += new System.EventHandler(this.GraphCreatingForm_Shown);
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem файлToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem создатьToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem открытьToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem сохранитьToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem выйтиToolStripMenuItem;
private System.Windows.Forms.PictureBox pictureBox;
private System.Windows.Forms.OpenFileDialog openFileDialog;
private System.Windows.Forms.SaveFileDialog saveFileDialog;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
}
}
using System.Drawing;
using System.Windows.Forms;
namespace ColoringGraphProblem.Views.Dialogs
{
public class DialogForm : Form
{
private bool canBeClosed;
private readonly bool defaultValue;
private void MyFormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = !canBeClosed;
}
public void MakeUnsizeable()
{
MaximizeBox = false;
MinimizeBox = false;
MaximumSize = new Size(Width, Height);
MinimumSize = new Size(Width, Height);
}
public DialogForm(bool canBeClosedByUser)
{
FormClosing += MyFormClosing;
defaultValue = canBeClosedByUser;
canBeClosed = defaultValue;
}
public DialogForm() : this(true)
{
}
public void SuperClose()
{
canBeClosed = true;
Close();
canBeClosed = defaultValue;
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// DialogForm
//
this.ClientSize = new System.Drawing.Size(116, 16);
this.Name = "DialogForm";
this.ResumeLayout(false);
}
}
}
namespace ColoringGraphProblem.Views.Dialogs
{
public partial class ErrorMessageForm : DialogForm
{
public ErrorMessageForm()
{
InitializeComponent();
MakeUnsizeable();
}
public void SetErrorMessage(string message)
{
textBox.Text = message;
}
private void textBox_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
if (e.KeyChar.CompareTo('\r') == 0)
okButton.PerformClick();
}
}
}
namespace ColoringGraphProblem.Views.Dialogs
{
partial class ErrorMessageForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.textBox = new System.Windows.Forms.TextBox();
this.okButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// textBox
//
this.textBox.Location = new System.Drawing.Point(13, 13);
this.textBox.Multiline = true;
this.textBox.Name = "textBox";
this.textBox.ReadOnly = true;
this.textBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.textBox.Size = new System.Drawing.Size(259, 208);
this.textBox.TabIndex = 0;
this.textBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.textBox_KeyPress);
//
// okButton
//
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(197, 227);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 1;
this.okButton.Text = "ОК";
this.okButton.UseVisualStyleBackColor = true;
//
// ErrorMessageForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Controls.Add(this.okButton);
this.Controls.Add(this.textBox);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ErrorMessageForm";
this.Text = "Произошла ошибка";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox textBox;
private System.Windows.Forms.Button okButton;
}
}
using System.Windows.Forms;
using ColoringGraphProblem.Views.Dialogs;
namespace ColoringGraphProblem.Views.CommonForms
{
public class ErrorMessageModel
{
private readonly ErrorMessageForm form;
public ErrorMessageModel()
{
form = new ErrorMessageForm();
}
public void ShowMessage(IWin32Window owner, string message)
{
form.SetErrorMessage(message);
form.ShowDialog(owner);
}
}
}
using ColoringGraphProblem.Exceptions;
using ColoringGraphProblem.Views.Dialogs;
namespace ColoringGraphProblem.Views.CommonForms
{
public partial class InputIntegerForm : DialogForm
{
//модель
private readonly InputIntegerModel model;
//Загрузить заголовок формы
public void SetFormCaption(string text)
{
Text = text;
}
//Загрузить текст сообщения
public void SetInputMessage(string text)
{
inputMessageLabel.Text = text;
}
//
public int GetInputInteger()
{
int res;
if (!int.TryParse(intTextBox.Text, out res))
throw new UserException("Выражение '{0}' не является целым числом.", intTextBox.Text);
return res;
}
public InputIntegerForm(InputIntegerModel model)
{
this.model = model;
InitializeComponent();
MakeUnsizeable();
}
private void intTextBox_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
if (e.KeyChar.CompareTo('\r') == 0)
okButton.PerformClick();
}
}
}
namespace ColoringGraphProblem.Views.CommonForms
{
partial class InputIntegerForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.intTextBox = new System.Windows.Forms.TextBox();
this.okButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button();
this.inputMessageLabel = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// intTextBox
//
this.intTextBox.Location = new System.Drawing.Point(9, 24);
this.intTextBox.Name = "intTextBox";
this.intTextBox.Size = new System.Drawing.Size(204, 20);
this.intTextBox.TabIndex = 0;
this.intTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.intTextBox_KeyPress);
//
// okButton
//
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(9, 50);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(106, 23);
this.okButton.TabIndex = 1;
this.okButton.Text = "ОК";
this.okButton.UseVisualStyleBackColor = true;
//
// cancelButton
//
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(121, 50);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(92, 23);
this.cancelButton.TabIndex = 2;
this.cancelButton.Text = "Отмена";
this.cancelButton.UseVisualStyleBackColor = true;
//
// inputMessageLabel
//
this.inputMessageLabel.AutoSize = true;
this.inputMessageLabel.Location = new System.Drawing.Point(6, 8);
this.inputMessageLabel.Name = "inputMessageLabel";
this.inputMessageLabel.Size = new System.Drawing.Size(207, 13);
this.inputMessageLabel.TabIndex = 3;
this.inputMessageLabel.Text = "Введите что-то очень важное и нужное:";
//
// InputIntegerForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(219, 78);
this.Controls.Add(this.inputMessageLabel);
this.Controls.Add(this.cancelButton);
this.Controls.Add(this.okButton);
this.Controls.Add(this.intTextBox);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "InputIntegerForm";
this.Text = "caption";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox intTextBox;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Label inputMessageLabel;
}
}
using System.Windows.Forms;
using ColoringGraphProblem.Views.CommonForms;
namespace ColoringGraphProblem.Views.Dialogs
{
public class InputIntegerModel
{
private readonly InputIntegerForm form;
public InputIntegerModel(string formCaption, string inputMessage)
{
form = new InputIntegerForm(this);
form.SetFormCaption(formCaption);
form.SetInputMessage(inputMessage);
}
//есть 3 варианта возращаемого результата: число - пользователь ввел что-то разумное, null - пользователь нажал отмена, UserException - пользователь сделал не так
public int? GetValue(IWin32Window owner)
{
if (form.ShowDialog(owner) == DialogResult.OK) return form.GetInputInteger();
return null;
}
}
}