Мои первые шаги в SWT: Простенький блокнот со вкладками

На Хабрахабре очень мало статей о SWT, поэтому я постараюсь исправить данное маленькое упущение.

Из данной статьи Вы узнаете:
  • Какова мотивация использовать SWT в отличии от основного конкурента — Swing
  • Основные трудности с которыми я столкнулся при разработке простенького блокнота на связке Java + SWT и Немного кода
  • Каким образом упаковать и распространить свое приложение для нескольких платформ

Если Вас заинтересовало — прошу под кат.

Преамбула


Как известно, Java славится своей кросс-платформенностью (write-once — run anywhere), и к GUI приложениям это тоже относится. Хотя, пожалуй, Java и не самая распространненная платформа для написания GUI приложений. И на то есть ряд причин.

Пожалуй основная причина тому — убеждение, что код написанный под какую-то конкретную платформу будет работать быстрее, нежели код, который написан для чего-то абстрактного в вакууме. Что ж, на мой взгляд пожалуй это правда.

Некоторые со мной не согласятся и скажут, что это как раз стиль Java — написанное единожды должно исполняться и выглядеть одинаково на всех платформах. И Swing тому хорошее подтверждение. Конечно же Swing приложение можно кастомизировать с помощью различных тем(Look And Feel) но все равно оно будет чувствоваться и выглядеть не совсем так, как родное.

Хотя ребятам из JetBrains и удалось доказать (по крайней мере мне лично), что Swing приложения могут быть очень-даже дружелюбными, но факт остается фактом — оно начинает со временем тормозить не потому что написано плохо, а именно в силу того, что в качестве GUI фреймворка используется Swing, который с операционными системами дружит несколько хуже чем их нативные компоненты.

SWT как раз подошел с другой стороны к этой проблеме. По большому счету SWT — это тонкая прослойка между Java программистом и нативными API какой-то конкретной операционной ситемы, что несколько наршует идеологию Java. Но, как говорится, правила как раз и существуют для того, что бы их нарушать. Особенно, если это идет на благо. И это на мой взгляд является основной мотивацией, почему стоит обратить внимание на SWT.

Первые шаги


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

Задуммка была весьма проста:
Я хотел сделать подобие классического блокнота, того что мы наблюдаем под Windows. Только со вкладками.

Забегая вперед скажу, что я это не полностью реализовал свою идею — отстутствуют возможности посылать документ принтеру на печать, возможность менять шрифт, поиск по документу. Но, то что получилось, позволило почувствовать «вкус SWT», который окалазся довольно-таки приятным.

Ну что же. Пока что было только много слов, но никаких действий. Пора привести немножко кода.

Точка входа в приложение, это(Bootstrap.java):

    final Display display = new Display();
	final Shell shell = new Shell(display);
	final DocumentAndTabManager documentAndTabManager = new DocumentAndTabManager(shell);
	final TextEditor textEditor = new TextEditor(shell, documentAndTabManager);
	textEditor.init();
	textEditor.run();


Что мы здесь сделали:
  • Создали дисплей(Display), сказали что бы оболочка(Shell) использовала наш дисплей. (На мой взгляд SWT имеет достаточно стройную идеологию — приложению не следует иметь более чем один объект-дисплей.)
  • Оболочка(Shell) — то, где будет проходить основное действие. Если провести аналогию со Swing — то можно сказать, что это в какой-то мере аналог JPanel.
  • Сделали инъекцию из оболочки нашему мэнеджеру документов и редактору текста, что бы они не забывались, зачем они нужны и кто у них хозяин.


Проинициализировали оболочку (TextEditor.java):


    shell.setLayout(new FillLayout());

	//И поставили поставили меню оболочке(окошку):

	shell.setMenuBar(createAndSetUpMenu());


В кратце процесс создания меню:

    //Говорим что у нас есть такое-то меню, чей родитель - оболочка:
	final Menu menu = new Menu(shell, SWT.BAR);

	// Создаем пункт в меню.
	final MenuItem helpItem = new MenuItem(menu, SWT.CASCADE);
	//Ставим текст: знак "&" говорит нам о том, что пункт меню будет доступен по комбинации клавиш Alt+H.
	helpItem.setText("&Help");

	//Далее делаем под-меню для нашего меню Item'a:
	final Menu helpMenu = new Menu(menu);
	helpItem.setMenu(helpMenu);
	final MenuItem about = new MenuItem(helpMenu, SWT.NONE);
	about.setText("About");
	about.addSelectionListener(new HelpMenuAboutSelectionListener(shell));//Вешаем слшуатель события на нажатие этого пункта меню.
	


И запускаем наше приложение:

    shell.open();//Отображаем окно

	while (!shell.isDisposed()) {
	    final Display display = shell.getDisplay();
	        if (!display.readAndDispatch()) {
	            display.sleep();
	        }
	}

	shell.dispose();


Далее, во время выполнения нашего блокнота, сработает какой-то из слушателей (в ответ на какое-то наше действие), из которого мы сделаем то, что нам нужно, допустим запустим какое-то задание в синхронном или в асинхронном режиме:

    shell.getDisplay().asyncExec(new ReloadJob(documentAndTabManager, currentTab));


Упаковка


Очередная порция псевдо-трудностей с которой я столкнулся:
Для каждой операционной системы и архитектуры нужно сделать свой билд. Для этого нужны jar'ы для каждой конкретной платформы. Я привык пользоваться Maven'ом, но для этих целей он не очень хорошо подходит, ибо свежих версий SWT я увы не нашел в Maven-репозиториях, да и пришлось скачивать мануально пакеты для каждой из платформ операционных систем/архитектур. Кроме того там где нужно много кастомизации Maven не особо подходит, зато на помощь приходит старый-добрый Ant. В качестве менеджера зависимостей я взял Ivy.

Кусок ant-скрипта(build.xml), отвечающий за упаковку под конкретную операционную систему/архитектуру:

    <jar destfile="${package.dir}/${jar.prefix}_${target.platform}-${target.architecture}-${version}.jar"
	             basedir="${output.dir}">
	     <restrict>
	         <archives>
	                    <zips>
	                 <fileset refid="classpath.files"/>
	                 <fileset dir="${basedir}/lib/swt" includes="swt-${target.platform}-${target.architecture}.jar"/>
	             </zips>
	         </archives>
	    </restrict>
	    <manifest>
	         <attribute name="Main-Class" value="swt.texteditor.simple.Bootstrap"/>
	    </manifest>
	</jar>
       


Размер Jar'ов получился 2 — 2.5 МБ (в зависимости от ОС и архитектуры), что вообщем-то не так уж и много.

Вместо заключения


Пропустив через себя небольшой цикл разработки я могу заметить

Минусы SWT на мой взгляд:
* Мало документации/туториалов
* Гугл не так много знает о SWT, как о Swing
* Многие проблемы приходится решать дольше.

Плюсы SWT на мой взгляд:
* В общем и целом простой и понятный API
* Кросс-платформенное, нативно выглядящее приложение
* Из коробки быстрее работающее, чем такое же на Swing

Что я вынес для себя из этого маленького эксперимента?

SWT со всеми своими прелестями и недостатками произвел на меня приятное впечатление. Когда я в следующий раз решусь написать десктопное приложение — я без сомнения выберу связку Java + SWT.

P.S. Несколько скриншотов напоследок.

Из-под Linux'a:

Нативная прокрутка для Ubuntu (Unity)


Нативное меню для Ubuntu (Unity)


И из-под Windows:



Под Mac OS к сожалению скриншотов нет за неимением Mac OS X.
Любезно предоставленный скриншот пользователем seneast:
image

Исходные коды доступны здесь: code.google.com/p/swt-text-editor/source/browse
Скачать мою вариацией блокнота для своей ОС можно здесь: code.google.com/p/swt-text-editor/downloads/list
Из-под Windows запускается двойным кликом, из-под Mac OS X java -XstartOnFirstThread -jar simple_edit*., Linux командой java -jar simple_edit*. Запускается под Java 1.6 и выше.
Короткие инструкции, как начать баловаться с кодом из-под IDE(на английском) code.google.com/p/swt-text-editor/wiki/SourceCodeImport

Буду рад любым комментариям, нареканиям и советам.
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 40

    +3
    Из под Mac OS X
    $ java -jar simple_edit_macosx_cocoa-64-0.1.jar

    ***WARNING: Display must be created on main thread due to Cocoa restrictions.
    Exception in thread «main» org.eclipse.swt.SWTException: Invalid thread access
    at org.eclipse.swt.SWT.error(Unknown Source)
    at org.eclipse.swt.SWT.error(Unknown Source)
    at org.eclipse.swt.SWT.error(Unknown Source)
    at org.eclipse.swt.widgets.Display.error(Unknown Source)
    at org.eclipse.swt.widgets.Display.createDisplay(Unknown Source)
    at org.eclipse.swt.widgets.Display.create(Unknown Source)
    at org.eclipse.swt.graphics.Device.(Unknown Source)
    at org.eclipse.swt.widgets.Display.(Unknown Source)
    at org.eclipse.swt.widgets.Display.(Unknown Source)
    at swt.texteditor.simple.Bootstrap.main(Unknown Source)
      0
      Спасибо, к сожалению под Linux/Windows у меня таких проблем не возникло. Попробую в ближайшее время раздобыть Mac OS X и разобраться в чем там проблема.
        +2
        У меня возникали такие проблемы, с помощью JarBundler смог разрулить всё. Если интересно — взгляните здесь: code.google.com/p/fa13jogador, проект, для которого применял сборку SWT-приложения под Mac
          +1
          спасибо
        +1
        К сожалению, не нашел на чем проверифицировать, но я так понимаю, что для Mac пользователей должно помочь добавление флага -XstartOnFirstThread:
        java -XstartOnFirstThread -jar simple_edit_macosx_cocoa-64-0.1.jar
          +3
          Помогло, можете добавить скрин в статью.
            +1
            Спасибо за скриншот.
        +2
        Два вопроса:
        1) Почему не Jambi (Qt)?
        2) Зачем самому иметь все три платформы для сборки, когда зависимости от эклипса можно разруливать мавеном?
          +2
          1). Был интерес именно пощупать фреймворк, на котором построен такой гигант, как Eclipse
          2). Как я уже говорил, я не нашел последнии версии swt (3.7) в Maven репозиториях. То, что я нашел, было версии 3.3.0, что не очень-то хорошо(2011 год на дворе как ни как). К тому же Maven хорошо ездит по рельсам, а если нужно сделать что-то кастомное, то тут приходится извращаться. Конкретно пришлось бы использовать в любом случае Ant, только уже из-под Maven'a использовать maven-antrun-plugin, который бы дергал какой-то target, который продуцировал бы билды для разных платформ.
            0
            Да, про старость версий я не заметил. Для меня это не было проблемой — просто раздеплоил на свой артифактори все версии клипца, а теперь просто подцепляю их через профили.
              0
              Конкретно пришлось бы использовать в любом случае Ant, только уже из-под Maven'a использовать maven-antrun-plugin, который бы дергал какой-то target, который продуцировал бы билды для разных платформ.

              А executions нельзя настроить для assembly-плагина? Я так понял, что для вас мавен это то, что зависимости скачивает. Правда он сам по себе отличная билд тула. Просто нужно уметь пользоваться.
                0
                Я плохо знаком с Maven Assembly плагином, но из краткого знакомства с документацией, я вынес, что мне предлагается создать 7 профайлов(для каждой из операционных систем и для каждой из архитектур[32/64 бита]) и написать 7 дескрипторов(по дескриптору на профайл). На мой взгляд выходит несколько громоздкое решение для моей задачи.

                У меня с Maven все хорошо пока, он катится по рельсам и ты лишь немного направляешь его. Но с ним становится все плохо, когда нужно его кастомизировать. Мое решение на Ant + Ivy у меня заняло в сумме 263 + 11 = 274 строк кода. А сколько строк заняло бы аналогичное решение на Maven?
                  0
                  Но с ним становится все плохо, когда нужно его кастомизировать.

                  Вы просто не умеет его готовить :) Рекомендую полистать www.sonatype.com/books/mvnref-book/reference/. Просветляет почему те или иные вещи сделаны так, как они сделаны.

                  Я плохо знаком с Maven Assembly плагином, но из краткого знакомства с документацией, я вынес, что мне предлагается создать 7 профайлов(для каждой из операционных систем и для каждой из архитектур[32/64 бита]) и написать 7 дескрипторов(по дескриптору на профайл). На мой взгляд выходит несколько громоздкое решение для моей задачи.

                  Не, можно просто запаблишить артифакт для каждой платформы с определённым классификатором(linux32, linux64, etc) и дёргать его в зависимости от операционной системы.

                  Мое решение на Ant + Ivy у меня заняло в сумме 263 + 11 = 274 строк кода. А сколько строк заняло бы аналогичное решение на Maven?

                  Хватит заниматься гольфом :) Это актуально, если мы пишем библиотеку и хотим уменьшить количество строк в код, который её использует. Но при дизайне портируемого билда определённо не стоит считать. Тем более любая ide валидирует pom-файлы и умеет автодополнение. Ну пусть будет много, зато расширяемо.
                    0
                    Возможно, я учту ваши замечания когда буду писать кросс-платформенного клиента для своего сервиса. В любом случае, спасибо за ваши замечания и рекомендации. :)
                • UFO just landed and posted this here
              • UFO just landed and posted this here
                  0
                  А как же qt.gitorious.org/qt-jambi/qtjambi-community?
                  Последний коммит буквально вчера…
                  • UFO just landed and posted this here
                      0
                      Ну так по мне — это круче. Куче плюсов и очень немного минусов.

                      Плюсами я называю свободную лицензию, большое количество заинтересованных в развитии и открытость.
                +4
                stackoverflow.com/questions/2706222/create-cross-platform-java-swt-application — ещё вот эта штука может быть весьма полезной. Тут описано, как сделать универсальный jar-ник для всех платформ. Уже в SWT-FAQ внесено, кстати.

                Сам просто на днях в этой теме вновь копался, выступал с докладом на JavaDay новосибирском :)
                  +1
                  Спасибо
                  +1
                  Я привык пользоваться Maven'ом, но для этих целей он не очень хорошо подходит, ибо свежих версий SWT я увы не нашел в Maven-репозиториях, да и пришлось скачивать мануально пакеты для каждой из платформ операционных систем/архитектур. Кроме того там где нужно много кастомизации Maven не особо подходит, зато на помощь приходит старый-добрый Ant. В качестве менеджера зависимостей я взял Ivy.

                  Ну классификаторы же. Зачем писать миллион первый ант скрипт?!!!
                    +1
                    Хм… Может я чего-то не знаю, но Swing тоже умеет делать нативный интерфейс. Причём он может натягивать его динамически. Во время исполнения пробует натянуть интерфейс и поведение той системы, в которой запущен. Если что-то не так — откат на стандартный…

                    image

                    image
                      0
                      Вы сами себе противоречите. Взгляните на свой скриншот из-под MacOs: где же он нативный? Взять хотя бы меню!
                      А под Linux, так вообще тошнит от его «нативности».
                        0
                        И ещё скроллбар. Вертикального градиента не должно быть — только серый ползунок.
                          +1
                          У меня во всех приложениях, вот в Safari ещже этот градиент.
                          0
                          Это самое что ни на есть меню!
                          Просто я не люблю синие градиенты. Вот моё меню Finder — скрин.
                          Обратите внимание на скроллбар в TextArea.

                          Вот так это выглядит в стандартном режиме:
                          не нативно
                            0
                            И вот опять тоже самое. Почему у Finder меню вверху, где и положено, а у вашего приложения в окне? Это уже не нативно.
                            А сколлбар должен выгляжеть вот так.
                              0
                              нет не должен. А про меню вверху я вообще ничего не знаю, в топике есть скрин SWT под маком, там тоже меню не на месте. Я же не про Swing — Cocoa говорю, а про Swing — SWT.
                                0
                                > в топике есть скрин SWT под маком, там тоже меню не на месте
                                Это где? Там всего 1 скриншот из-под мака и меню в окне не видно.
                                Зато на скриншоте из-под Ubuntu явно видно, что оно вверху.
                                  0
                                  Упс, спутал. На виндовом смотрел…
                                  +2
                                  В топике, на скриншоте под Mac OS, меню вообще не отображается потому что скриншот не на весь экран, и не видно меню, которое отображается на панеле так же, как в Unity оболочке Ubuntu. Если вы владелец Mac OS, то можете скачать и запустить у себя приложение — больше чем уверен, что меню отобразится вверху, на панеле, там где отображают его другие нативные приложения. Да и как я уже пытался заметить, основная проблема в том, что Swing не использует нативные компоненты, а эмулирует их. Отсюда ряд ограничений, деградация производительности и прочие сопутствующие вещи при __прочих равных условиях__.
                          0
                          Внесу свою скромную лепту в виде комментариев по заключению…

                          Минусы SWT на мой взгляд:
                          * Мало документации/туториалов

                          Как мне кажется — у них как раз проще всего найти пример на тот или иной случай:
                          www.eclipse.org/swt/snippets/
                          Это как вариант — есть и множество примеров от сторонних людей.

                          * Гугл не так много знает о SWT, как о Swing

                          Могу сказать, что о Swing толковой информации также мало как и об SWT — я не говорю о примерах уровня «Hello World» коих множество на всё что угодно.

                          * Многие проблемы приходится решать дольше.

                          Тут сложно поспорить — что-то на SWT в отличие от Swing просто-напросто убивает время.

                          Плюсы SWT на мой взгляд:
                          * В общем и целом простой и понятный API

                          У них действительно есть множество готовых решений под различные случаи, которые зачастую сильно упрощают разработку, в отличии от реализации подобной вещи в том же Swing. Однако стоит выйти за рамки стандартных кнопок-табов и начинаются проблемы…

                          * Кросс-платформенное, нативно выглядящее приложение

                          На Вашем же примере табы приложения выглять ненативно на всех 3ёх ОС (что, кстати, странно). Скажем тот же Swing предоставляет на каждой ОС свой (весьма схожий с нативным) Look and Feel — мне они нравятся даже больше, нежели оформление некоторых компонентов в SWT.

                          * Из коробки быстрее работающее, чем такое же на Swing

                          Вопрос про скорость весьма спорный — я бы сказал даже про Swing — «Вы просто не умеете его готовит» :)
                          Скорости интерфейса на Swing должно хватать для любого десктоп приложения (для адекватной работы, естественно) — различные оптимизации работы интерфейса успешно делают своё дело. А вот нативный интерфейс, кстати говоря, ест побольше ресурсов машины при работе, как я смог заметить по нескольким тестам (да, он всегда отзывчив, не подвисает даже при нагрузках, но ресурсов потребляет больше).

                          В общем и целом — попробовав написать приложение на SWT — я сперва обрадовался, но потом потихоньку стал сползать на вставки Swing-частей в SWT, а затем полностью ушёл от SWT, так как смысла в нём никакого уже не было — он лишь нагружал приложение излишними библиотеками.

                          В SWT есть лишь несколько полезных вещей, которые очень бы хотелось в адекватном виде видеть в J2SE:
                          — Нормальная работа с меню в трее (сейчас, как ни крути — придётся использовать устаревшее PopupMenu для корректного его закрывания — в нём даже иконок не установить толком)
                          — Возможность создания ToolDialog'ов (нативных ToolDialog'ов — это отдельный тип окон — есть во всех известных ОС), более
                          — Более удобная работа с модальностью диалогов (т.е. хотелось бы иметь возможность легко и быстро создать модальный диалог, блокирующий лишь одно окно приложения, а не все)
                          — Нормальная работа прозрачности окон на Unix-системах (сейчас там полный бордак — корректно данная возможность представлина лишь на Windows/MacOS)

                          Впрочем все эти пункты, как можно заметить, не настолько критичны, чтобы бросать Swing и погружаться в SWT. Собственно именно поэтому я отказался от разработки десктоп-приложений на SWT в пользу Swing.
                            0
                            Кстати, я надеюсь до новогодних праздников выложить две достаточно масштабные статьи на тему — будет возможность оценить «силу» Swing и различные его возможности. Думаю по ним Вы сможете примерно прикинуть под какие цели лучше Swing, а под какие SWT.
                              0
                              Насчет табов — я использовал CTabs и CTabFolder вместо простых Tab и TabFolder. Пожалуй это тоже сыграло свое дело.
                                0
                                Похоже что так.
                                  0
                                  Ярлыки у CTabFolder можно раскрасить в более подходящие цвета (как это делает сама Eclipse).
                                    0
                                    Можно конечно, но TabFolder/Tabs выглядят из коробки нативно, в то время как CTabFolder/CTabs уже сразу подразумевают тот факт, что они кастомные.
                                0
                                Ну и ещё стоит сказать, что писать на чистом SWT — несколько старомодно. Гораздо удобнее пользоваться надстройкой JFace.
                                Например, создание меню на JFace выглядит попрозрачнее. А также работа с таблицами и деревьями.
                                  0
                                  Посмотрел код. Ну, для учебной программы сойдёт, хотя уж очень она какая-то учебная. Зачем так много журналирования, непонятно. Зачем надо запускать задачи по asyncExec (это в главном-то треде) — тоже непонятно. Ну и в целом структура классов…

                                  Only users with full accounts can post comments. Log in, please.