Графические движки: Как стать "тигром"

Как стать "тигром"

При создании сложных графических систем моделирования встает вопрос о графическом "движке". Большинство программистов пишут все с нуля. А что делать, когда проект нужен еще вчера или через пару дней нужно сдавать курсовой? Ответ прост: "Зачем изобретать велосипед? Особенно если его давно уже изобрели и раздают всем желающим даром". Место, где "велосипеды за мальчиков дают", называется http://sourceforge.net/ . Это просто огромный "склад велосипедов":). А если серьезно, то только на просмотр списка проектов можно потратить уйму времени. Шутка ли, только по Java таковых наберется более десятка тысяч.

Одним из таких проектов является программа для UML-проектирования ArgoUML. Эта свободная программа стала основой для нескольких коммерческих проектов — например, PoseidonUML. Мы же не планируем создавать такие "крутые" вещи, хотя в тихом омуте...
Рассмотрим возможность использования графического движка данной разработки, а именно пакета org.tigris.gef (graph editing framework). Jar-архив gef.jar, а также пакет протоколирования log4j.jar, который требуется для его работы, можно скачать со страницы http://argouml.tigris.org/ .
Сразу хочется обратить ваше внимание на наличие нескольких демонстрационных программ и апплетов в каталоге org.tigris.gef.demo (по крайней мере, в составе ранних билдов "тигров" таковые присутствовали). Ну, а мы, как у нас принято, "пойдем своим путем", но по протоптанной дорожке. Так как только на собственных ошибках можно прочувствовать все нюансы и восхититься открывшимися нам возможностями. Хочется обратить ваше внимание на то, что предлагаемый проект был написан за несколько дней исключительно для тестирования и, возможно, содержит баги. Ну, а так как мы с вами присоединяемся к сообществу тигров, то код демонстрационной программы полностью отдается вам на растерзание. И будем надеяться, что в итоге этих терзаний на свет родятся новые замечательные программные продукты.

Для начала создадим главный модуль приложения Main.java. Первое, что преподносит нам библиотека gef — это класс org.tigris.gef.util. Localizer. Назначением данного класса, как нетрудно догадаться, является локализация ваших творений на разные языки. Нам нужно лишь добавить виды ресурсов и их адреса, а также выбрать нужную локаль, например:

ResourceLoader.addResourceExtension("gif");
ResourceLoader.addResourceLocation("/by/bs/gefdemo/resource");
Localizer.addResource("GefDemo", "by.bs.gefdemo.resource.GefDemo");
Locale locale = Locale.getDefault();
Localizer.addLocale(locale); //set default user locale
Localizer.switchCurrentLocale(locale);

Данный класс создает хэш-таблицу ресурсов в памяти и позволяет использовать несколько языков, а также локализовывать картинки. При этом повторный запрос не требует дисковых чтений. А так как данный класс статический, то при первом вызове создается единственный экземпляр данного объекта на всю программу, и у нас больше не болит голова во время правки кода. Ведь весь доступ к ресурсам пишется, как любят говорить в армии, "однообразно", например:

mmFile.setText(Localizer.localize("GefDemo", "MainFrame.mmFile"));

Для извлечения ресурса указываем "раздел" и "ключ", по которым производится поиск. Маппинг на реальный каталог и поиск ресурса производится автоматически.
Попробуем создать программу моделирования архитектуры локальной сети. Создадим три объекта, моделирующие компьютер, хаб и сетевой принтер. UML-диаграмма объектов приложения показана на рис. 1.
Визуальной оболочкой нашей программы будет форма MainFrame.java. Для удобства отладки в визуальных IDE средах проектирования типа JBuilder или Oracle JDeveloper добавлен метод jbInit(), инициализирующий динамические объекты формы. Форма включает в себя меню, панели выбора объектов, панель графического редактора и панель статуса. Особенностью формирования меню доступных операций является использование конструктора вида JMenuItem (Action), где в качестве Action используются готовые события библиотеки gef, наследующие класс org.tigris.gef.base.Cmd. Например:

private JMenuItem mmiPrint = new JMenuItem(new CmdPrint());
private JMenuItem mmiPageSetup = new JMenuItem(new CmdPrintPageSetup (new CmdPrint()));

Сердцем графического редактора является панель типа org.tigris.gef.graph.presentation.JGraph. Именно на ней сконцентрировано большинство возможностей редактора. Для отображения сообщений главная форма должна реализовывать интерфейс org.tigris.gef. ui.IstatusBar, имеющий один метод:

public abstract void showStatus(String s);

Так как в нашем приложении требуется связь между графическими объектами, то создадим объекты Computer Node, Hub4Node и PrinterNode, наследующие класс org.tigris. gef.graph.presentation.NetNode. Этот класс представляет собой сетевой узел, имеющий порты (ports), посредством которых осуществляется соединение с другими узлами. При создании нового экземпляра узла вызывается метод инициализации. В нашем случае именно здесь нужно создать порты, например:

public void initialize(Hashtable args) {
addPort(fDataPort = new PortData (this));
}
Наследуя класс org.tigris.gef. graph.presentation.NetPort, можно создать свой тип порта, но мы воспользуемся готовым классом org.tigris.gef.demo. PortData из демонстрационного проекта. Для визуального представления создаваемого нами узла предназначен метод makePresentation, возвращающий объект, представляющий заданную фигуру. Например:

public FigNode makePresentation (Layer lay) {
//Объекты, для прорисовки фигуры
org.tigris.gef.presentation.Fig obj1 =
new FigRect(0, -30, 60, 60, Color.black, Color.lightGray);
org.tigris.gef.presentation.Fig obj2 =
new FigRect(5, -25, 50, 40, Color.black, Color.white);
org.tigris.gef.presentation.Fig obj3 =
new FigRect(5, 30, 50, 5, Color.black, Color.lightGray);
org.tigris.gef.presentation.Fig light =
new FigRect(50, 20, 5, 5, Color.black, Color.green);
//Рисуем порт
FigRRect dataPort =
new FigRRect(-8, 10, 8, 8, Color. black, Color.black);
dataPort.setCornerRadius(3);
//Вектор объектов, из которых состоит фигура
Vector objList = new Vector();
objList.addElement(obj1);
objList.addElement(obj2);
objList.addElement(obj3);
objList.addElement(light);
objList.addElement(dataPort);
FigNode figNode = new FigNode(this, objList);
//Для возможности связи с другими портами нужно зарегистрировать порт
figNode.bindPort(fDataPort, dataPort);
return figNode;
}
Выбор помещаемых в графический редактор объектов будем осуществлять при помощи панели кнопок. Создадим класс NetworkToolbar, наследующий org.tigris.gef.ui.ToolBar. Для добавления кнопок предназначен метод defineButtons(). Например:

public void defineButtons() {
add(new CmdSetMode(org.tigris.gef. base.ModeSelect.class, "Select"));
add(new CmdSetMode(org.tigris.gef. base.ModeBroom.class, "Broom"));
addSeparator();
add(new CmdCreateNode(by.bs.gef demo.PrinterNode.class,"GefDemo","PrinterNode"));
add(new CmdCreateNode(by.bs.gef demo.Hub4Node.class, "GefDemo", "Hub 4Node"));
add(new CmdCreateNode(by.bs.gef demo.ComputerNode.class, "GefDemo", "ComputerNode"));
}

Добавляем созданную панель в JtabbedPane, компилируем проект, и результат наших нехитрых манипуляций виден налицо. Не особенно утруждая себя созданием графического редактора, мы получили довольно многофункциональное приложение. Большую часть работы сделали за нас ребята сообщества тигров. Пример результата наших трудов показан на рис. 2.
Архив проекта и дополнительные библиотеки ПО можно скачать по адресам http://berdachuk.at.tut.by/downloads/gefdemo.zip, http://berdachuk.at.tut.by/downloads/gefdemoaddlib.zip .

Сергей Бердачук,
Berdachuk@tut.by http://berdachuk.at.tut.by


Рис. 1. UML-модель классов


Рис. 2. Пример созданной в нашем приложении модели локальной сети


Компьютерная газета. Статья была опубликована в номере 12 за 2004 год в рубрике программирование :: разное

©1997-2024 Компьютерная газета