Приветствую хабросообщество!
Наверно каждый кто профессионально разрабатывает ПО или просто увлекается программированием, рано или поздно приходил к необходимости создавать пользовательский интерфейс для своей программы. И если не рассматривать нативные для платформ окружения и языки такие как C# для Windows или Objective-C для Mac OS X которые изначально содержат средства для визуализации интерфейса, то выбор оказывается не очень богатым, особенно если мы не горим желанием платить деньги за средства разработки GUI или желаем добиться кроссплатформенности.
В своем первом посте на Хабре я бы хотел рассказать о таком фремворке как JUCE. Поиск по Хабру выдал всего 2 статьи где данный фреймворк только упоминается, но ни какой детальной информации не приводится. Думаю что тем кто только начинает осваивать кроссплатформенные GUI приложения на C++ будет интересно узнать об альтернативе таким монстрам как Qt или таким старичкам как GTK+
Что такое JUCE
Официальный сайт разработчиков: juce.com.
JUCE (Jules' Utility Class Extensions) это всеохватывающая библиотека классов С++ для разработки кроссплатформенного программного обеспечения.
Он содержит практически все что вам может понадобиться для создания большинства приложений, особенно хорошо подходит для построения сложных GUI, обработки графики и аудио.
Juce — это открытый кроссплатформенный инструментарий разработки ПО (фреймворк) для языка C++, используемый для разработки GUI приложений и плагинов.
JUCE поддерживает следующие платформы и возможности:
- Mac OS X Приложения и VST/AudioUnit/RTAS/NPAPI плагины компилируются при помощи Xcode. (на 10.7.5 & Xcode 4.6.3 все работает без проблем)
- Windows Приложения и VST/AudioUnit/RTAS/NPAPI/ActiveX плагины собираются при помощи MS Visual Studio. Результаты полностью совместимы с Windows XP, Vista и Win7/8
- Linux Приложения и плагины могут быть собраны для любого ядра версии 2.6 и старше.
- iOS Нативные iPhone и iPad приложения собираются при помощи Xcode
- Android Android приложения собираются при помощи Ant или Eclipse с использованием Android NDK.
JUCE разрабатывался силами одного человека и из набора классов для личного использования перерос в полноценный фреймворк.
OpenSource лицензия появилась в 2003 году, а с 2005 фреймворк обзавелся платной ее версией для закрытого ПО.
Большинство модулей фреймворка имеют открытую лицензию GPLv2, v3 и AGPLv3, модуль Core имеет лицензию ISC
Если у вас нет желания публиковать исходный код своей программы, то вы можете приобрести коммерческую лицензию по следующим расценкам:
- Лицензия на один продукт — 399 Английских Фунтов
- Лицензия на неограниченное колличество продуктов — 699 Фунтов
- Апгрейд с первой на вторую — 349 Фунтов
- Апгрейд старых версий — до полной 349 Фунтов до лицензии на один продукт 199 Фунтов
Состав фреймворка и начало работы
Сам JUCE достаточно легковесный, чуть более 28 Мб. Папка docs содержит подробную документацию по установке и подключению фреймворка в различных ОС и средах разработки.
Касательно установки стоит заметить что JUCEпредставляет собой не набор библиотек в привычном смысле или набор исходников из которых вы должны собрать библиотеки для статической или динамической линковки, а набор заголовочных файлов и файлов исходного кода, содержащие все предоставляемые фреймворком классы. Данный подход не только упрощает миграцию с одной платформы на другую, избавляя вас от необходимости включать в состав дистрибутивов библиотеки фреймворка или требовать их наличия на компьютере пользователя, но и решает проблему разрядности библиотек от которых зависит фреймворк. Так как стандартные библиотеки или стандартное окружение современных ОС которое используется в проектах JUCE (все они описываются в документации поставляемой в комплекте с фреймворком) как правило собраны с поддержкой как 32 так и 64 bit. Так что вам стоит следить за битность только тех библиотек которые вы дополнительно используете в своем проекте.
Все модули фреймворка находятся в папке modules. Весь исходный код хорошо структурирован и комментирован, так что проблем с пониманием возникнуть не делжно.
В папке extras можно найти примеры использования фреймворка. Среди прочего там же вы найдете две утилиты, которые помогут вам в разработке. Для этих утилит, да и для всех примеров, уже есть готовые проекты для различных платформ и IDE, все что вам остается это открыть проект и скомпилировать пример или утилиту.
Первая утилита это Introjucer, достаточно аскетичная программа для автоматической генерации проектов, написана с использованием самого JUCE и является ярким примером возможностей фреймворка. В комплекте присутствует мастер по созданию проектов, мастер по созданию шаблонов исходно кода и очень весомое дополнение — визуальный редактор GUI, который просто генерирует на основе ваших манипуляций код на С++ в соответствующий файл исходного кода (который до того был создан при помощи мастера шаблонов), редактор кода, мастер локализации и ряд других достаточно полезных инструментов.
Редактор кода
Редактор GUI
Добавление sourcecode файлов
Приятной изюменкой в Introjucer является наличие собственного файла проекта, настроенного под все IDE и платформы которые поддерживаются фреймворком. Для переноса кода от разработчика не потребуется ни чего, кроме наличи на целевой системе утилиты Introjucer. Каждый проект сформированный утилитой можно открыть в целевой IDE которую вы используете для своей ОС.
Вторая утилита это binarybuilder, которая позволяет вам перенести бинарные файлы непосредственно в код ваших исходных файлов. На пример картинки и логотипы превратятся в набор бинарных данных которые вы можете использовать в своем коде. Эта утилита так же является частью Introjucer и используется там в менеджере ресурсов при редактировании GUI. Но может быть собрана как отдельное консольное приложение.
Hello World
И куда же без примера кода…
Рассмотрим подробнее пример поставляемый с фреймворком:
../extras/example projects:
В проектах JUCE все заголовочные файлы в конечном итоге вкладываются в один файл "../JuceLibraryCode/JuceHeader.h".
Так что разработчику не приходится подключать весь ворох классов из фреймворка вручную в каждом исходном файле.
В рассматриваемом примере присутствует два модуля программы:
- Main.cpp который отвечает не посредственно за само наше приложение — старт, вывод окна на экран, завершение и т.д.
- MainComponent.cpp(.h) который отвечает за наполнение нашего окна — кнопки, фон, поля для ввода текста и т.д.
Стоит заметить что в JUCE все содержимое GUI является классом, наследуемым от класса JUCE Component.
Рассмотрим наш MainComponent.h:
#ifndef __JUCE_HEADER_9002020A4DD09B20__
#define __JUCE_HEADER_9002020A4DD09B20__
//Подключаем наш фреймворк
#include "../JuceLibraryCode/JuceHeader.h"
//И описываем наш класс
class MainComponent : public Component, //Наследуем от основного класса фреймворка
public ButtonListener //данный клас отвечает за прослушивание событий кнопок интерфейса
Далее требуется описать поведение класса, его конструкторы и деструкторы.
В секции public находится следующий код:
MainComponent ();
~MainComponent();
void paint (Graphics& g); //метод отвечающий за отрисовку в canvas
void resized(); //метод вызываемый при изменении размера
void buttonClicked (Button* buttonThatWasClicked); //метод вызываемый при нажатии на кнопку
В секции private находятся непосредственно наши элементы интерфейса:
ScopedPointer<Label> helloWorldLabel;
ScopedPointer<TextButton> quitButton;
Path internalPath1;
Перейдем непосредственно к логике работы нашего GUI в MainComponent.cpp:
//Не забываем подключить заголовочный файл нашего класса
#include "MainComponent.h"
//Добовляем логику для конструктора
MainComponent::MainComponent ()
{
addAndMakeVisible (helloWorldLabel = new Label (String::empty,
"Hello World!")); //Добавляем наш текстовый лэйбл, делаем его видимым и добавляем дополнительные параметры
helloWorldLabel->setFont (Font (40.00f, Font::bold));
helloWorldLabel->setJustificationType (Justification::centred);
helloWorldLabel->setEditable (false, false, false);
helloWorldLabel->setColour (Label::textColourId, Colours::black);
helloWorldLabel->setColour (TextEditor::textColourId, Colours::black);
helloWorldLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (quitButton = new TextButton (String::empty)); //То же самое делаем с нашей кнопкой
quitButton->setButtonText ("Quit");
quitButton->addListener (this);
setSize (600, 300); //Задаем размер нашего содержимого
}
//Деструктор просто уничтожает содержимое нашего GUI
MainComponent::~MainComponent()
{
helloWorldLabel = nullptr;
quitButton = nullptr;
}
//И описываем все методы нашего класса
void MainComponent::paint (Graphics& g)
{
g.fillAll (Colour (0xffc1d0ff));
g.setColour (Colours::white);
g.fillPath (internalPath1);
g.setColour (Colour (0xff6f6f6f));
g.strokePath (internalPath1, PathStrokeType (5.200f));
}
void MainComponent::resized()
{
helloWorldLabel->setBounds (152, 80, 296, 48);
quitButton->setBounds (getWidth() - 176, getHeight() - 60, 120, 32);
internalPath1.clear();
internalPath1.startNewSubPath (136.0f, 80.0f);
internalPath1.quadraticTo (176.0f, 24.0f, 328.0f, 32.0f);
internalPath1.quadraticTo (472.0f, 40.0f, 472.0f, 104.0f);
internalPath1.quadraticTo (472.0f, 192.0f, 232.0f, 176.0f);
internalPath1.lineTo (184.0f, 216.0f);
internalPath1.lineTo (200.0f, 168.0f);
internalPath1.quadraticTo (96.0f, 136.0f, 136.0f, 80.0f);
internalPath1.closeSubPath();
}
void MainComponent::buttonClicked (Button* buttonThatWasClicked)
{
if (buttonThatWasClicked == quitButton)
{
JUCEApplication::quit();
}
}
На этом наш класс готов к тому что бы поселиться в окне приложения. Перейдем к Main.cpp и посмотрим каким образом инициализируется и запускается наш пример.
//Поключаем наш фреймворк и класс контента
#include "../JuceLibraryCode/JuceHeader.h"
#include "MainComponent.h"
//Описываем окно нашего приложения
class HelloWorldWindow : public DocumentWindow
{
public:
HelloWorldWindow(): DocumentWindow ("JUCE Hello World!",
Colours::lightgrey,
DocumentWindow::allButtons,
true)
{
// Заполняем окно нашим содержимым
setContentOwned (new MainComponent(), true);
// Задаем положение по центру
centreWithSize (getWidth(), getHeight());
// И отображаем его
setVisible (true);
}
~HelloWorldWindow()
{
// Описываем деструктор если он нам нужен
}
void closeButtonPressed()
{
// Описываем реакцию на нажатие кнопки закрытия
JUCEApplication::quit();
}
};
Окно мы описали, теперь очередь за самим приложением:
class JUCEHelloWorldApplication : public JUCEApplication //Наследуем от основного класса приложения JUCE
{
public:
JUCEHelloWorldApplication() {}
void initialise (const String& commandLine)
{
// Инициализируем приложение создавая наше окно
helloWorldWindow = new HelloWorldWindow();
}
void shutdown()
{
// Описываем поведение при закрытии приложения
helloWorldWindow = nullptr;
}
//Далее задаются методы для получения некоторой информации о приложении
const String getApplicationName()
{
return "Hello World for JUCE";
}
const String getApplicationVersion()
{
// ProjectInfo::versionString автоматически обновляется Jucer, и
// описывается в JuceHeader.h который генерируется для нашего проекта
return ProjectInfo::versionString;
}
bool moreThanOneInstanceAllowed() //Разрешаем запуск нескольких версий
{
return true;
}
void anotherInstanceStarted (const String& commandLine)
{
}
private:
ScopedPointer<HelloWorldWindow> helloWorldWindow; //свойства класса нашего приложения, по сути все окна которые в нем содержаться
// Этот макрос создает точку входа main() нашего приложения.
START_JUCE_APPLICATION (JUCEHelloWorldApplication)
Все готово, если наш проект собрать и запустить то мы получаем вот такой результат:
Если вы желаете посмотреть на что еще способен JUCE рекомендую собрать и запустить приложение Demo из папки extras, в котором показаны основные возможности данного фреймворка, но не забывайте что их гораздо больше и все зависит от того как вы примените то что имеете.
Полезные ссылки
- Официальный сайт
- Страница на GitHub
- Страница на Sourceforge
- Официальная страница с документацией
- Juce Programming Tutorial достаточно подробное описание разработки приложения с использованием JUCE
Послесловие
Надеюсь кому то мой экспромт окажется полезным.
Мое личное мнение таково: данный фреймворк заслуживает внимания и того, что бы использоваться в малых и больших проектах.
С удовольствием приму любую критику и замечания, спасибо что обратили внимание на данный пост!
Автор: GunGraveKoga