Цель лекции: Научиться использовать классы кнопок Juce в своих программах.
Кнопки представляют, пожалуй, одну из наиболее часто используемых групп виджетов. Базовым классом для них является Button, которому наследуют классы кнопки с текстом TextButton (пример ее использования рассматривался нами в лекции 4 и многих последующих главах), кнопки со стрелкой ArrowButton, кнопок с изображениями (, кнопки панели инструментов (ToolbarItemComponent), а также ряд компонентов, которые имеют по своим функциям лишь косвенное отношение к кнопкам: гиперссылка (HyperlinkButton), флажок (ToggleButton), который рассматривался нами в лекции 11 и вкладка (TabBarButton).
TextButton — это кнопка со стандартными (для Juce) формой и фоном и отображаемой на поверхности строкой текста. Кнопка TextButton может работать в двух режимах: простая управляющая кнопка (включено по умолчанию) и кнопка с фиксацией нажатого состояния. Для включения последнего режима необходимо вызвать метод void Button::setClickingTogglesState(bool shouldToggle) throw() с параметром true. Кроме того, возможно использование набора кнопок с фиксацией нажатого состояния, где в нажатом (on) состоянии может находиться одна и только одна кнопка. Для создания подобной группы используют методы void Button::setToggleState(bool shouldBeOn, bool sendChangeNotification) для задания режима фиксации и void Button::setRadioGroupId(int newGroupId) для включения кнопок в единую группу путем задания общего ID. Использование этих методов совершенно идентично таковому у ToggleButton (см. лекцию 11) .
Следует заметить, что, в отличие от других библиотек для построения графического интерфейса пользователя, в Juce кнопка в нажатом состоянии меняет только цвет фона. Цвета кнопки в нажатом (on) и отжатом (off) состоянии определяются значениями идентификаторов цвета enum TextButton::ColourIds:
buttonColourId — цвет фона кнопки в ненажатом состоянии;buttonOnColourId — цвет фона кнопки в нажатом состоянии;textColourOffId — цвет текста кнопки в ненажатом состоянии;textColourOnId — цвет текста кнопки в нажатом состоянии.Задать необходимое значение цвета фона либо текста кнопки можно вызовом метода void Component::setColour(int colourId, const , передав в качестве параметра один из идентификаторов ColourIds.
Основное свойство с точки зрения внешнего вида кнопки с текстом — это, конечно же, надпись. Для работы с ней класс TextButton включает два метода, унаследованные от класса Button:
void Button::setButtonText(const String newText изменяет надпись на кнопке на newTextconst String Button::getButtonText() const— возвращает текст, отображаемый на кнопке.Как правило, программист сам заботится о том, чтобы размеры кнопки и отображаемой ею надписи соответствовали друг другу. Однако в случае, если надпись на кнопке меняется в процессе выполнения программы, может возникнуть необходимость "подогнать" размеры кнопки под новый текст. В этом поможет метод ).
(рис 12.1) Программа, иллюстрирующая работу кнопки с текстом и изменяемой шириной
При нажатии на кнопку надпись последней будет меняться на текст в поле ввода при его наличии. При этом ширина кнопки будет меняться в зависимости от размеров новой надписи, а высота будет оставаться прежней
#include "TCentralComponent.h"
//---------------------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//---------------------------------------------------------------------
TCentralComponent::TCentralComponent() : Component ("Central Component"),
pTextButton(0),
pTextEdit(0)
{
pTextButton = new TextButton("Text Button");
pTextButton->addListener(this);
pTextButton->setButtonText(tr("Введи новый текст и нажми кнопку"));
addAndMakeVisible(pTextButton);
pTextEdit = new TextEditor("Text Editor");
pTextEdit->setReturnKeyStartsNewLine(false);
pTextEdit->setText(String::empty);
addAndMakeVisible(pTextEdit);
setSize (400, 150);
}
//---------------------------------------------------------------------
TCentralComponent::~TCentralComponent()
{
// Удаляем дочерние виджеты
// и обнуляем их указатели
deleteAllChildren();
}
//---------------------------------------------------------------------
void TCentralComponent::paint(Graphics Canvas)
{
Canvas.fillAll(Colours::azure);
}
//---------------------------------------------------------------------
void TCentralComponent::resized()
{
// Задаем стартовые размеры кнопки
pTextButton->setBounds(20, 16, 240, 25);
// "Подгоняет" размеры кнопки под отображаемую надпись
pTextButton->changeWidthToFitText(-1);
pTextEdit->setBounds(20, 56, getWidth() - 40, 25);
}
//---------------------------------------------------------------------
void TCentralComponent::buttonClicked(Button* pButton)
{
// Если нажата кнопка...
if(pButton == pTextButton)
{
// и поле ввода не пусто...
if(!pTextEdit->getText().containsNonWhitespaceChars()) return;
// в качестве надписи устанавливаем
// текст из поля ввода
pTextButton->setButtonText(pTextEdit->getText());
// "Подгоняет" размеры кнопки под отображаемую надпись
pTextButton->changeWidthToFitText(-1);
}
}
//---------------------------------------------------------------------
Обратите внимание, что метод changeWidthToFitText должен вызываться дважды: в обработчике щелчка по кнопке buttonClicked, и в функции resized; в противном случае при каждом изменении размеров окна, например, растягивании, кнопка будет возвращаться к начальной форме, заданной параметрами метода setBounds.
Зачастую в надписи на кнопке один из символов подчеркнут. Это означает, что пользователь может нажать комбинацию клавиш <ALT>+<S>, где <S> - это подчеркнутый символ, и это будет эквивалентно щелчку по кнопке. Добиться этого можно с помощью метода void Button::addShortcut(const KeyPress key), где key — код "горячей" клавиши с или без модификатора (<CTRL>, <ALT>, <SHIFT> и т.п.). Например, если нам нужно, чтобы обработчик щелчка по кнопке pCloseButton срабатывал при нажатии клавиш <ALT>+<Q>, необходимо вызвать метод addShortcut следующим образом:
pCloseButton->addShortcut(KeyPress('q', ModifierKeys::altModifier, 0))
В этом примере мы создали новый объект класса KeyPress, отслеживающего нажатие клавиш клавиатуры, в конструктор которого в качестве параметров передали код "горячей" клавиши ('q') и ее модификатора. Третий параметр — печатаемый символ (в нашем случае равен нулю, т.к. вызывающий виджет не является полем ввода).
В свою очередь, созданный объект класса KeyPress передается в метод addShortcut в качестве параметра. Ниже приведены некоторые идентификаторы enum ModifierKeys:
shiftModifier — флаг нажатия на клавишу <SHIFT>;ctrlModifier — флаг нажатия на клавишу <CTRL>;altModifier — флаг нажатия на клавишу <ALT>.Помимо этого, ModifierKeys содержит модификаторы, связанные с различными манипуляциями кнопками мыши.
Разумеется, можно зарегистрировать для кнопки "горячую" клавишу безо всякого модификатора. Например, если в качестве параметра в метод addShortcut кнопки "Отменить" передать в качестве параметра KeyPress::escapeKey, то нажатие пользователем клавиши <ESC> будет эквивалентно щелчку по данной кнопке. Это целесообразно делать в различных диалоговых окнах, чтобы пользователь мог их закрывать без использования мыши.
Для того, чтобы отключить все "горячие" клавиши, зарегистрированные для данной кнопки, используют метод void Button::clearShortcuts().
Основное событие любой кнопки — это щелчок по ней. При этом вызывается виртуальный защищенный метод ). Метод является чистой виртуальной (
Практически все, сказанное выше, относится и к другим кнопкам.
).
(рис 12.2) Внешний вид кнопки со стрелкой (параметр конструктора arrowDirection принимает значение 0.75; arrowColour — Colours::blue)
Конструктор кнопки ArrowButton::ArrowButton(const String buttonName, float arrowDirection, const принимает три параметра:
buttonName — имя кнопки;arrowDirection — направление кнопки, где при принимаемом значении 0.0 стрелка направлена вправо, 0.25 — вниз, 0.5 — влево, а 0.75 — вверх;arrowColour — цвет стрелки.DrawableButton — это кнопка, которая отображает один или несколько объектов класса, производного от Drawable. Drawable — это базовый класс для объектов, которые могут рисовать сами на себе графические примитивы (полигоны) или изображения (например, загруженные из файла). Поскольку Drawable является абстрактным классом, включающим ряд чистых виртуальных функций-членов, на практике используют производный от него класс, DrawableImage объект которого представляет собой битовое изображение (bitmap).
При необходимости возможно использование других производных от Drawable классов, например, DrawableShape или DrawableText.
Конструктор DrawableButton::DrawableButton(const String buttonName, создает "пустую", т.е. не содержащую никаких изображений кнопку.
В качестве параметров конструктор принимает строку — имя кнопки (buttonName) и ее стиль, идентификатор enum DrawableButton::.
Стили у DrawableButton могут быть следующими:
ImageFitted — кнопка отображает объекты Drawable, изменяя их размеры таким образом, чтобы изображения помещались внутри кнопки;ImageRaw — кнопка показывает изображения с их исходными размерами;ImageOnButtonBackground — рисуется стандартная для Juce кнопка с наложенным поверх изображением Drawable.Следует помнить, что программист должен сам позаботиться о соответствии размеров изображения, отображаемого DrawableImage и размеров кнопки DrawableButton.
Рассмотрим использование ).
(рис 12.3) Изображения для кнопки DrawableButton, подготовленные в GIMP и cохраненные в виде файлов *.png
В качестве основы для нашей программы используем код из вышеприведенного примера, заменив класс у .
#include "TCentralComponent.h"
//---------------------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//---------------------------------------------------------------------
TCentralComponent::TCentralComponent() : Component ("Central Component"),
pHelloLabel(0),
pCloseButton(0)
{
pHelloLabel = new Label("Hello Label", tr("Привет, мир!"));
addAndMakeVisible(pHelloLabel);
pHelloLabel->setFont(Font(38.0000f, Font::bold));
pHelloLabel->setJustificationType(Justification::centred);
pHelloLabel->setEditable(false, false, false);
pHelloLabel->setColour(Label::textColourId, Colours::blue);
pHelloLabel->setColour(TextEditor::textColourId, Colours::black);
pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);
// Получаем рабочий каталог программы и сохраняем путь к нему
// в виде строки
String sPath = File::getCurrentWorkingDirectory().getFullPathName();
// Полное имя файла изображения ненажатой кнопки
String sUp;
// Полное имя файла изображения нажатой кнопки
String sDown;
// Получаем полные имена файлов изображений для Windows и Linux
#if JUCE_WINDOWS
sUp = sPath + "\\gimpbutton-up.png";
sDown = sPath + "\\gimpbutton-down.png";
#elif JUCE_LINUX
sUp = sPath + "/gimpbutton-up.png";
sDown = sPath + "/gimpbutton-down.png";
#endif
// Изображение для ненажатой кнопки
DrawableImage UpImage;
// Изображение для нажатой кнопки
DrawableImage DownImage;
// Загружаем изображение в DrawableImage из соответствующего файла
UpImage.setImage(ImageCache::getFromFile(File(sUp)));
DownImage.setImage(ImageCache::getFromFile(File(sDown)));
// Создаем пустую DrawableButton
pCloseButton = new DrawableButton("CloseButton",
DrawableButton::ImageAboveTextLabel);
// Устанавливаем изображения для различных состояний кнопки
pCloseButton->setImages( UpImage, UpImage, DownImage);
addAndMakeVisible(pCloseButton);
pCloseButton->setButtonText(tr("Закрыть"));
pCloseButton->addListener(this);
setSize (400, 200);
}
//---------------------------------------------------------------------
TCentralComponent::~TCentralComponent()
{
deleteAllChildren();
}
//---------------------------------------------------------------------
void TCentralComponent::paint(Graphics Canvas)
{
Canvas.fillAll(Colours::azure);
}
//---------------------------------------------------------------------
void TCentralComponent::resized()
{
pHelloLabel->setBounds(0, 0, proportionOfWidth(1.0000f), proportionOfHeight(0.9000f));
pCloseButton->setBounds(getWidth() - 10 - 204, getHeight() - 60, 204, 53);
}
//---------------------------------------------------------------------
void TCentralComponent::buttonClicked(Button* pButton)
{
if (pButton == pCloseButton)
{
JUCEApplication::quit();
}
}
//---------------------------------------------------------------------
В конструкторе класса компонента содержимого мы создаем "пустую" кнопку DrawableButton, которая будет показывать надпись "Закрыть" под отображаемыми картинками.
Для того, чтобы наша кнопка pCloseButton могла показывать изображения, созданные в GIMP, мы должны получить их из объектов класса DrawableImage с помощью метода
void DrawableButton::setImages( const Drawable* normalImage, const Drawable* overImage = 0, const Drawable* downImage = 0, const Drawable* disabledImage = 0, const Drawable* normalImageOn = 0, const Drawable* overImageOn = 0, const Drawable* downImageOn = 0, const Drawable* disabledImageOn = 0 ).
Смысл передаваемых параметров понятен из их названий:
normalImage — изображение кнопки в ее "нормальном", ненажатом состоянии. Единственный параметр, значение которого не может быть равным нулю;overImage — изображение кнопки при наведении на нее мыши. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage;downImage — изображение кнопки в нажатом состоянии. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение overImage;disabledImage — изображение кнопки в недоступном состоянии. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage с увеличенной прозрачностью;normalImageOn — то же самое, что и normalImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage;overImageOn — то же самое, что и overImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImageOn;downImageOn — то же самое, что и downImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение overImageOn;disabledImageOn — то же самое, что и disabledImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано нормальное изображение с увеличенной прозрачностью.Поскольку мы подготовили лишь два файла изображений для кнопки рис 12.3, то, соответственно, мы будем передавать в метод setImages лишь два объекта класса DrawableImae — UpImage для ненажатой и DownImage — для нажатой кнопки.
Загружать изображения из графических файлов в объекты .
В свою очередь, метод getFromFile в качестве параметра получает объект класса File, предназначенного для манипуляций с локальными файлами и директориями. В качестве параметра конструктора объект класса получает строку, содержащую абсолютный путь к файлу.
Чтобы сформировать эту строку, мы получаем вначале текущую рабочую папку нашей программы (напомним, что файлы загружаемых изображений должны находиться именно в ней) с помощью метода .
Внешний вид работающей программы показан на рисунке 12.4 .
(рис 12.4) Программа, иллюстрирующая работу DrawableButton
Наша программа имеет ряд мелких, но существенных недостатков: во-первых, есть риск что используемые файлы изображений могут быть удалены по ошибке, а во-вторых, метод getCurrentWorkingDirectory() может возвращать разные значения в зависимости от условий запуска исполняемого файла нашей программы: например, в в Windows при запуске для отладки в среде разработки; в Linux — при запуске не из командной строки, а щелчком мышью по значку файла. Все это, хоть и не вызовет краха программы, но приведет к неправильному отображению закрывающей кнопки.
Поэтому удобнее, если загружаемые изображения будут "зашиты" в самом исполняемом файле программы.
Добавлять те или иные изображения в программу в виде платформо-независимых ресурсов в Juce можно двумя способами: во-первых, если вы остановились на визуальном проектировании компонента, можно воспользоваться возможностями jucer и, во-вторых, можно использовать консольную утилиту BinaryBuilder, входящую в поставку Juce.
В первом случае бинарные ресурсы, описывающие изображения, будут добавлены в класс создаваемого компонента в виде его членов, а во втором — для них будет создано отдельное пространство имен (namespace).
Для того, чтобы добавить бинарный ресурс в класс компонента, нужно в jucer создать новый компонент (). В объявление класса будут добавлены две переменнные (static const char* и static const int), описывающие само изображение и его размер, и соответствующие имени ресурса, отображаемого на вкладке jucer. Например, если вы выбрали изображение picture.png, то название ресурса будет picture_png, а имена переменных static const char* picture_png и static const int picture_pngSize.
(рис 12.5) Добавление бинарного ресурса в jucer
Для того, чтобы добавить бинарные ресурсы в виде отдельного пространства имен, как упоминалось выше, необходимо использовать консольную утилиту BinaryBuilder. Она находится в папке <каталог Juce>/juce/extras/binarybuilder. Как и другие утилиты, входящие в поставку juce, BinaryBuilder распространяется в виде исходных текстов, поэтому программу требуется предварительно откомпилировать.
Запущенная без параметров, утилита BinaryBuilder выводит краткую справку по своему использованию. Запускать программу следует со следующими параметрами:
BinaryBuilder sourcedirectory targetdirectory targetclassnam
где
sourcedirectory — папка, в которой находятся добавляемые изображения или другие бинарные ресурсы;targetdirectory — папка, в которой будут сохранены генерируемые файлы исходных текстов;targetclassname — название пространства имен бинарных ресурсов.И в том, и в другом случае бинарные данные графического файла переводятся в форму обычного массива C++ .
static const unsigned char ucGimpbuttondown_png[] =
{137,80,78,71,13,10,26,10,0,0 ...
В случае использования juce этот массив добавляется в конец реализации класса компонента содержимого, а в случае использования BinaryBuilder — в
const char* TImagesFactory::psGimpbuttondown_png = (const char*)ucGimpbuttondown_png;
Рассмотрим использование бинарных ресурсов для создания . Как видно из листинга 12.5, реализация класса компонента содержимого практически не изменилась: мы лишь подключили пространство имен TImagesFactory и воспользовались методом static const Image ImageCache::getFromMemory(const void* imageData, int для загрузки изображений в DrawableImage не из графического файла, а из памяти.
#include "TCentralComponent.h"
// Подключаем пространство имен TImagesFactory
#include "TImagesFactory.h"
//---------------------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//---------------------------------------------------------------------
TCentralComponent::TCentralComponent() : Component ("Central Component"),
pHelloLabel(0),
pCloseButton(0)
{
pHelloLabel = new Label("Hello Label", tr("Привет, мир!"));
addAndMakeVisible(pHelloLabel);
pHelloLabel->setFont(Font(38.0000f, Font::bold));
pHelloLabel->setJustificationType(Justification::centred);
pHelloLabel->setEditable(false, false, false);
pHelloLabel->setColour(Label::textColourId, Colours::blue);
pHelloLabel->setColour(TextEditor::textColourId, Colours::black);
pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);
// Изображение для ненажатой кнопки
DrawableImage UpImage;
// Изображение для нажатой кнопки
DrawableImage DownImage;
// Загружаем изображение в DrawableImage из памяти
UpImage.setImage(ImageCache::getFromMemory(
TImagesFactory::psGimpbuttonup_png,
TImagesFactory::iGimpbuttonup_pngSize));
DownImage.setImage(ImageCache::getFromMemory(
TImagesFactory::psGimpbuttondown_png,
TImagesFactory::iGimpbuttondown_pngSize));
// Создаем пустую DrawableButton
pCloseButton = new DrawableButton("CloseButton",
DrawableButton::ImageAboveTextLabel);
// Устанавливаем изображения для различных состояний кнопки
pCloseButton->setImages( UpImage, UpImage, DownImage);
addAndMakeVisible(pCloseButton);
pCloseButton->setButtonText(tr("Закрыть"));
pCloseButton->addListener(this);
setSize (400, 200);
}
//---------------------------------------------------------------------
Откомпилируем новую версию программы и убедимся, что по своему виду и функциональности она ничем не отличается от предыдущей рис 12.4, но лишена ее недостатков.
Как уже отмечалось выше, с помощью ряда классов — потомков Drawable — DrawableButton может отображать не только готовые изображения, но и различные графические примитивы. Пример создания сложных кнопок DrawableButton в виде звезд с произвольным числом лучей с использованием класса DrawablePath входит в поставку Juce (см. <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp).
В данном примере DrawableButton легко можно заменить на ShapeButton — кнопку, содержащую залитую геометрическую фигуру (shape). Установить эту фигуру можно с помощью метода void ShapeButton::setShape(const Path newShape, bool resizeNowToFitThisShape, bool maintainShapeProportions, bool hasDropShadow), где newShape — собственно, рисуемая фигура (объект класса Path), а maintainShapeProportions и hasDropShadow — флаги того, должны ли размеры кнопки подстраиваться под размеры фигуры, и того, должна ли фигура отбрасывать тень, соответственно.
Мы не будем подробно останавливаться на работе с , поскольку она сходна с таковой для DrawableButton, разница лишь в использовании объектов класса Image, а не Drawable в методе setImages плюс задание цветов кнопки при манипуляции с ней мышью (наведение и т.п.). Пример использования имеется в демонстрационном приложении Juce (см. <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp).
ToolbarItcodeComponent — кнопка инструментальной панели — подробно рассмотрена в лекции 15 (комоненты панели инструментов).
HyperlinkButton — кнопка, отображающая подчеркнутую гиперссылку на ресурс Web, который запускается в браузере, установленном по умолчанию, при щелчке по ней. Особенностью кнопки является то, что для ее работы не нужен слушатель (ButtonListener), хотя его установка и возможна, например, если вы хотите, чтобы были выполнены какие-либо действия перед переходом по ссылке.
Конструктор класса кнопки с гипертекстовой ссылкой HyperlinkButton::HyperlinkButton(const String linkText, const URL linkURL) принимает два параметра: linkText — отображаемый текст и linkURL — собственно гиперссылка, объект специального класса Juce, URL.
Ссылку на ресурс Web можно изменить с помощью метода void HyperlinkButton::setURL(const URL newURL ) throw().
Как и у любой кнопки, текст HyperlinkButton можно изменить методом setButtonText.
Пример использования HyperlinkButton представлен в листинге 12.6, внешний вид работающей программы показан на рисунке12.6
#include "TCentralComponent.h"
//---------------------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//---------------------------------------------------------------------
TCentralComponent::TCentralComponent() : Component("Central Component"),
pHyperlinkButton(0)
{
// Кнопка с гиперссылкой
pHyperlinkButton =
new HyperlinkButton(L"http://www.rawmaterialsoftware.com/juce.php",
URL(L"http://www.rawmaterialsoftware.com/juce.php"));
JuceHyperlinkButton // Устанавливаем текст всплывающей подсказки
pHyperlinkButton->setTooltip(
L"http://www.rawmaterialsoftware.com/juce.php");
addAndMakeVisible(pHyperlinkButton);
setSize(400, 200);
}
//---------------------------------------------------------------------
TCentralComponent::~TCentralComponent()
{
// Удаляем дочерний виджет
// и обнуляем его указатель
deleteAndZero(pHyperlinkButton);
}
//---------------------------------------------------------------------
void TCentralComponent::paint(Graphics Canvas)
{
Canvas.fillAll(Colours::white);
}
//---------------------------------------------------------------------
void TCentralComponent::resized()
{
pHyperlinkButton->setBounds(15, proportionOfHeight(0.4500f),
getWidth() - 15, 25);
}
//---------------------------------------------------------------------
(рис 12.6) Внешний вид программы с HyperlinkButton
В данной лекции нами рассмотрены классы, необходимые для создания кнопок различного вида: кнопок с текстом TextButton, кнопок со стрелкой ArrowButton, кнопок с изображениями (, DrawableButton и ShapeButton) и гиперссылки (HyperlinkButton). Базовым классом для всех кнопок является Button.
Напишите игру "Светофор": при нажатии на одну из трех кнопок, расположенных вертикально, она окрашивается в соответствующий цвет (красный, желтый или зеленый), а остальные — в темно-серый.
Архив с исходными текстами примеров Вы можете скачать здесь
Цель лекции: Научиться использовать классы кнопок Juce в своих программах.
Кнопки представляют, пожалуй, одну из наиболее часто используемых групп виджетов. Базовым классом для них является Button, которому наследуют классы кнопки с текстом TextButton (пример ее использования рассматривался нами в лекции 4 и многих последующих главах), кнопки со стрелкой ArrowButton, кнопок с изображениями (, кнопки панели инструментов (ToolbarItemComponent), а также ряд компонентов, которые имеют по своим функциям лишь косвенное отношение к кнопкам: гиперссылка (HyperlinkButton), флажок (ToggleButton), который рассматривался нами в лекции 11 и вкладка (TabBarButton).
TextButton — это кнопка со стандартными (для Juce) формой и фоном и отображаемой на поверхности строкой текста. Кнопка TextButton может работать в двух режимах: простая управляющая кнопка (включено по умолчанию) и кнопка с фиксацией нажатого состояния. Для включения последнего режима необходимо вызвать метод void Button::setClickingTogglesState(bool shouldToggle) throw() с параметром true. Кроме того, возможно использование набора кнопок с фиксацией нажатого состояния, где в нажатом (on) состоянии может находиться одна и только одна кнопка. Для создания подобной группы используют методы void Button::setToggleState(bool shouldBeOn, bool sendChangeNotification) для задания режима фиксации и void Button::setRadioGroupId(int newGroupId) для включения кнопок в единую группу путем задания общего ID. Использование этих методов совершенно идентично таковому у ToggleButton (см. лекцию 11) .
Следует заметить, что, в отличие от других библиотек для построения графического интерфейса пользователя, в Juce кнопка в нажатом состоянии меняет только цвет фона. Цвета кнопки в нажатом (on) и отжатом (off) состоянии определяются значениями идентификаторов цвета enum TextButton::ColourIds:
buttonColourId — цвет фона кнопки в ненажатом состоянии;buttonOnColourId — цвет фона кнопки в нажатом состоянии;textColourOffId — цвет текста кнопки в ненажатом состоянии;textColourOnId — цвет текста кнопки в нажатом состоянии.Задать необходимое значение цвета фона либо текста кнопки можно вызовом метода void Component::setColour(int colourId, const , передав в качестве параметра один из идентификаторов ColourIds.
Основное свойство с точки зрения внешнего вида кнопки с текстом — это, конечно же, надпись. Для работы с ней класс TextButton включает два метода, унаследованные от класса Button:
void Button::setButtonText(const String newText изменяет надпись на кнопке на newTextconst String Button::getButtonText() const— возвращает текст, отображаемый на кнопке.Как правило, программист сам заботится о том, чтобы размеры кнопки и отображаемой ею надписи соответствовали друг другу. Однако в случае, если надпись на кнопке меняется в процессе выполнения программы, может возникнуть необходимость "подогнать" размеры кнопки под новый текст. В этом поможет метод ).
(рис 12.1) Программа, иллюстрирующая работу кнопки с текстом и изменяемой шириной
При нажатии на кнопку надпись последней будет меняться на текст в поле ввода при его наличии. При этом ширина кнопки будет меняться в зависимости от размеров новой надписи, а высота будет оставаться прежней
#include "TCentralComponent.h"
//---------------------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//---------------------------------------------------------------------
TCentralComponent::TCentralComponent() : Component ("Central Component"),
pTextButton(0),
pTextEdit(0)
{
pTextButton = new TextButton("Text Button");
pTextButton->addListener(this);
pTextButton->setButtonText(tr("Введи новый текст и нажми кнопку"));
addAndMakeVisible(pTextButton);
pTextEdit = new TextEditor("Text Editor");
pTextEdit->setReturnKeyStartsNewLine(false);
pTextEdit->setText(String::empty);
addAndMakeVisible(pTextEdit);
setSize (400, 150);
}
//---------------------------------------------------------------------
TCentralComponent::~TCentralComponent()
{
// Удаляем дочерние виджеты
// и обнуляем их указатели
deleteAllChildren();
}
//---------------------------------------------------------------------
void TCentralComponent::paint(Graphics Canvas)
{
Canvas.fillAll(Colours::azure);
}
//---------------------------------------------------------------------
void TCentralComponent::resized()
{
// Задаем стартовые размеры кнопки
pTextButton->setBounds(20, 16, 240, 25);
// "Подгоняет" размеры кнопки под отображаемую надпись
pTextButton->changeWidthToFitText(-1);
pTextEdit->setBounds(20, 56, getWidth() - 40, 25);
}
//---------------------------------------------------------------------
void TCentralComponent::buttonClicked(Button* pButton)
{
// Если нажата кнопка...
if(pButton == pTextButton)
{
// и поле ввода не пусто...
if(!pTextEdit->getText().containsNonWhitespaceChars()) return;
// в качестве надписи устанавливаем
// текст из поля ввода
pTextButton->setButtonText(pTextEdit->getText());
// "Подгоняет" размеры кнопки под отображаемую надпись
pTextButton->changeWidthToFitText(-1);
}
}
//---------------------------------------------------------------------
Обратите внимание, что метод changeWidthToFitText должен вызываться дважды: в обработчике щелчка по кнопке buttonClicked, и в функции resized; в противном случае при каждом изменении размеров окна, например, растягивании, кнопка будет возвращаться к начальной форме, заданной параметрами метода setBounds.
Зачастую в надписи на кнопке один из символов подчеркнут. Это означает, что пользователь может нажать комбинацию клавиш <ALT>+<S>, где <S> - это подчеркнутый символ, и это будет эквивалентно щелчку по кнопке. Добиться этого можно с помощью метода void Button::addShortcut(const KeyPress key), где key — код "горячей" клавиши с или без модификатора (<CTRL>, <ALT>, <SHIFT> и т.п.). Например, если нам нужно, чтобы обработчик щелчка по кнопке pCloseButton срабатывал при нажатии клавиш <ALT>+<Q>, необходимо вызвать метод addShortcut следующим образом:
pCloseButton->addShortcut(KeyPress('q', ModifierKeys::altModifier, 0))
В этом примере мы создали новый объект класса KeyPress, отслеживающего нажатие клавиш клавиатуры, в конструктор которого в качестве параметров передали код "горячей" клавиши ('q') и ее модификатора. Третий параметр — печатаемый символ (в нашем случае равен нулю, т.к. вызывающий виджет не является полем ввода).
В свою очередь, созданный объект класса KeyPress передается в метод addShortcut в качестве параметра. Ниже приведены некоторые идентификаторы enum ModifierKeys:
shiftModifier — флаг нажатия на клавишу <SHIFT>;ctrlModifier — флаг нажатия на клавишу <CTRL>;altModifier — флаг нажатия на клавишу <ALT>.Помимо этого, ModifierKeys содержит модификаторы, связанные с различными манипуляциями кнопками мыши.
Разумеется, можно зарегистрировать для кнопки "горячую" клавишу безо всякого модификатора. Например, если в качестве параметра в метод addShortcut кнопки "Отменить" передать в качестве параметра KeyPress::escapeKey, то нажатие пользователем клавиши <ESC> будет эквивалентно щелчку по данной кнопке. Это целесообразно делать в различных диалоговых окнах, чтобы пользователь мог их закрывать без использования мыши.
Для того, чтобы отключить все "горячие" клавиши, зарегистрированные для данной кнопки, используют метод void Button::clearShortcuts().
Основное событие любой кнопки — это щелчок по ней. При этом вызывается виртуальный защищенный метод ). Метод является чистой виртуальной (
Практически все, сказанное выше, относится и к другим кнопкам.
).
(рис 12.2) Внешний вид кнопки со стрелкой (параметр конструктора arrowDirection принимает значение 0.75; arrowColour — Colours::blue)
Конструктор кнопки ArrowButton::ArrowButton(const String buttonName, float arrowDirection, const принимает три параметра:
buttonName — имя кнопки;arrowDirection — направление кнопки, где при принимаемом значении 0.0 стрелка направлена вправо, 0.25 — вниз, 0.5 — влево, а 0.75 — вверх;arrowColour — цвет стрелки.DrawableButton — это кнопка, которая отображает один или несколько объектов класса, производного от Drawable. Drawable — это базовый класс для объектов, которые могут рисовать сами на себе графические примитивы (полигоны) или изображения (например, загруженные из файла). Поскольку Drawable является абстрактным классом, включающим ряд чистых виртуальных функций-членов, на практике используют производный от него класс, DrawableImage объект которого представляет собой битовое изображение (bitmap).
При необходимости возможно использование других производных от Drawable классов, например, DrawableShape или DrawableText.
Конструктор DrawableButton::DrawableButton(const String buttonName, создает "пустую", т.е. не содержащую никаких изображений кнопку.
В качестве параметров конструктор принимает строку — имя кнопки (buttonName) и ее стиль, идентификатор enum DrawableButton::.
Стили у DrawableButton могут быть следующими:
ImageFitted — кнопка отображает объекты Drawable, изменяя их размеры таким образом, чтобы изображения помещались внутри кнопки;ImageRaw — кнопка показывает изображения с их исходными размерами;ImageOnButtonBackground — рисуется стандартная для Juce кнопка с наложенным поверх изображением Drawable.Следует помнить, что программист должен сам позаботиться о соответствии размеров изображения, отображаемого DrawableImage и размеров кнопки DrawableButton.
Рассмотрим использование ).
(рис 12.3) Изображения для кнопки DrawableButton, подготовленные в GIMP и cохраненные в виде файлов *.png
В качестве основы для нашей программы используем код из вышеприведенного примера, заменив класс у .
#include "TCentralComponent.h"
//---------------------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//---------------------------------------------------------------------
TCentralComponent::TCentralComponent() : Component ("Central Component"),
pHelloLabel(0),
pCloseButton(0)
{
pHelloLabel = new Label("Hello Label", tr("Привет, мир!"));
addAndMakeVisible(pHelloLabel);
pHelloLabel->setFont(Font(38.0000f, Font::bold));
pHelloLabel->setJustificationType(Justification::centred);
pHelloLabel->setEditable(false, false, false);
pHelloLabel->setColour(Label::textColourId, Colours::blue);
pHelloLabel->setColour(TextEditor::textColourId, Colours::black);
pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);
// Получаем рабочий каталог программы и сохраняем путь к нему
// в виде строки
String sPath = File::getCurrentWorkingDirectory().getFullPathName();
// Полное имя файла изображения ненажатой кнопки
String sUp;
// Полное имя файла изображения нажатой кнопки
String sDown;
// Получаем полные имена файлов изображений для Windows и Linux
#if JUCE_WINDOWS
sUp = sPath + "\\gimpbutton-up.png";
sDown = sPath + "\\gimpbutton-down.png";
#elif JUCE_LINUX
sUp = sPath + "/gimpbutton-up.png";
sDown = sPath + "/gimpbutton-down.png";
#endif
// Изображение для ненажатой кнопки
DrawableImage UpImage;
// Изображение для нажатой кнопки
DrawableImage DownImage;
// Загружаем изображение в DrawableImage из соответствующего файла
UpImage.setImage(ImageCache::getFromFile(File(sUp)));
DownImage.setImage(ImageCache::getFromFile(File(sDown)));
// Создаем пустую DrawableButton
pCloseButton = new DrawableButton("CloseButton",
DrawableButton::ImageAboveTextLabel);
// Устанавливаем изображения для различных состояний кнопки
pCloseButton->setImages( UpImage, UpImage, DownImage);
addAndMakeVisible(pCloseButton);
pCloseButton->setButtonText(tr("Закрыть"));
pCloseButton->addListener(this);
setSize (400, 200);
}
//---------------------------------------------------------------------
TCentralComponent::~TCentralComponent()
{
deleteAllChildren();
}
//---------------------------------------------------------------------
void TCentralComponent::paint(Graphics Canvas)
{
Canvas.fillAll(Colours::azure);
}
//---------------------------------------------------------------------
void TCentralComponent::resized()
{
pHelloLabel->setBounds(0, 0, proportionOfWidth(1.0000f), proportionOfHeight(0.9000f));
pCloseButton->setBounds(getWidth() - 10 - 204, getHeight() - 60, 204, 53);
}
//---------------------------------------------------------------------
void TCentralComponent::buttonClicked(Button* pButton)
{
if (pButton == pCloseButton)
{
JUCEApplication::quit();
}
}
//---------------------------------------------------------------------
В конструкторе класса компонента содержимого мы создаем "пустую" кнопку DrawableButton, которая будет показывать надпись "Закрыть" под отображаемыми картинками.
Для того, чтобы наша кнопка pCloseButton могла показывать изображения, созданные в GIMP, мы должны получить их из объектов класса DrawableImage с помощью метода
void DrawableButton::setImages( const Drawable* normalImage, const Drawable* overImage = 0, const Drawable* downImage = 0, const Drawable* disabledImage = 0, const Drawable* normalImageOn = 0, const Drawable* overImageOn = 0, const Drawable* downImageOn = 0, const Drawable* disabledImageOn = 0 ).
Смысл передаваемых параметров понятен из их названий:
normalImage — изображение кнопки в ее "нормальном", ненажатом состоянии. Единственный параметр, значение которого не может быть равным нулю;overImage — изображение кнопки при наведении на нее мыши. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage;downImage — изображение кнопки в нажатом состоянии. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение overImage;disabledImage — изображение кнопки в недоступном состоянии. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage с увеличенной прозрачностью;normalImageOn — то же самое, что и normalImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage;overImageOn — то же самое, что и overImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImageOn;downImageOn — то же самое, что и downImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение overImageOn;disabledImageOn — то же самое, что и disabledImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано нормальное изображение с увеличенной прозрачностью.Поскольку мы подготовили лишь два файла изображений для кнопки рис 12.3, то, соответственно, мы будем передавать в метод setImages лишь два объекта класса DrawableImae — UpImage для ненажатой и DownImage — для нажатой кнопки.
Загружать изображения из графических файлов в объекты .
В свою очередь, метод getFromFile в качестве параметра получает объект класса File, предназначенного для манипуляций с локальными файлами и директориями. В качестве параметра конструктора объект класса получает строку, содержащую абсолютный путь к файлу.
Чтобы сформировать эту строку, мы получаем вначале текущую рабочую папку нашей программы (напомним, что файлы загружаемых изображений должны находиться именно в ней) с помощью метода .
Внешний вид работающей программы показан на рисунке 12.4 .
(рис 12.4) Программа, иллюстрирующая работу DrawableButton
Наша программа имеет ряд мелких, но существенных недостатков: во-первых, есть риск что используемые файлы изображений могут быть удалены по ошибке, а во-вторых, метод getCurrentWorkingDirectory() может возвращать разные значения в зависимости от условий запуска исполняемого файла нашей программы: например, в в Windows при запуске для отладки в среде разработки; в Linux — при запуске не из командной строки, а щелчком мышью по значку файла. Все это, хоть и не вызовет краха программы, но приведет к неправильному отображению закрывающей кнопки.
Поэтому удобнее, если загружаемые изображения будут "зашиты" в самом исполняемом файле программы.
Добавлять те или иные изображения в программу в виде платформо-независимых ресурсов в Juce можно двумя способами: во-первых, если вы остановились на визуальном проектировании компонента, можно воспользоваться возможностями jucer и, во-вторых, можно использовать консольную утилиту BinaryBuilder, входящую в поставку Juce.
В первом случае бинарные ресурсы, описывающие изображения, будут добавлены в класс создаваемого компонента в виде его членов, а во втором — для них будет создано отдельное пространство имен (namespace).
Для того, чтобы добавить бинарный ресурс в класс компонента, нужно в jucer создать новый компонент (). В объявление класса будут добавлены две переменнные (static const char* и static const int), описывающие само изображение и его размер, и соответствующие имени ресурса, отображаемого на вкладке jucer. Например, если вы выбрали изображение picture.png, то название ресурса будет picture_png, а имена переменных static const char* picture_png и static const int picture_pngSize.
(рис 12.5) Добавление бинарного ресурса в jucer
Для того, чтобы добавить бинарные ресурсы в виде отдельного пространства имен, как упоминалось выше, необходимо использовать консольную утилиту BinaryBuilder. Она находится в папке <каталог Juce>/juce/extras/binarybuilder. Как и другие утилиты, входящие в поставку juce, BinaryBuilder распространяется в виде исходных текстов, поэтому программу требуется предварительно откомпилировать.
Запущенная без параметров, утилита BinaryBuilder выводит краткую справку по своему использованию. Запускать программу следует со следующими параметрами:
BinaryBuilder sourcedirectory targetdirectory targetclassnam
где
sourcedirectory — папка, в которой находятся добавляемые изображения или другие бинарные ресурсы;targetdirectory — папка, в которой будут сохранены генерируемые файлы исходных текстов;targetclassname — название пространства имен бинарных ресурсов.И в том, и в другом случае бинарные данные графического файла переводятся в форму обычного массива C++ .
static const unsigned char ucGimpbuttondown_png[] =
{137,80,78,71,13,10,26,10,0,0 ...
В случае использования juce этот массив добавляется в конец реализации класса компонента содержимого, а в случае использования BinaryBuilder — в
const char* TImagesFactory::psGimpbuttondown_png = (const char*)ucGimpbuttondown_png;
Рассмотрим использование бинарных ресурсов для создания . Как видно из листинга 12.5, реализация класса компонента содержимого практически не изменилась: мы лишь подключили пространство имен TImagesFactory и воспользовались методом static const Image ImageCache::getFromMemory(const void* imageData, int для загрузки изображений в DrawableImage не из графического файла, а из памяти.
#include "TCentralComponent.h"
// Подключаем пространство имен TImagesFactory
#include "TImagesFactory.h"
//---------------------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//---------------------------------------------------------------------
TCentralComponent::TCentralComponent() : Component ("Central Component"),
pHelloLabel(0),
pCloseButton(0)
{
pHelloLabel = new Label("Hello Label", tr("Привет, мир!"));
addAndMakeVisible(pHelloLabel);
pHelloLabel->setFont(Font(38.0000f, Font::bold));
pHelloLabel->setJustificationType(Justification::centred);
pHelloLabel->setEditable(false, false, false);
pHelloLabel->setColour(Label::textColourId, Colours::blue);
pHelloLabel->setColour(TextEditor::textColourId, Colours::black);
pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);
// Изображение для ненажатой кнопки
DrawableImage UpImage;
// Изображение для нажатой кнопки
DrawableImage DownImage;
// Загружаем изображение в DrawableImage из памяти
UpImage.setImage(ImageCache::getFromMemory(
TImagesFactory::psGimpbuttonup_png,
TImagesFactory::iGimpbuttonup_pngSize));
DownImage.setImage(ImageCache::getFromMemory(
TImagesFactory::psGimpbuttondown_png,
TImagesFactory::iGimpbuttondown_pngSize));
// Создаем пустую DrawableButton
pCloseButton = new DrawableButton("CloseButton",
DrawableButton::ImageAboveTextLabel);
// Устанавливаем изображения для различных состояний кнопки
pCloseButton->setImages( UpImage, UpImage, DownImage);
addAndMakeVisible(pCloseButton);
pCloseButton->setButtonText(tr("Закрыть"));
pCloseButton->addListener(this);
setSize (400, 200);
}
//---------------------------------------------------------------------
Откомпилируем новую версию программы и убедимся, что по своему виду и функциональности она ничем не отличается от предыдущей рис 12.4, но лишена ее недостатков.
Как уже отмечалось выше, с помощью ряда классов — потомков Drawable — DrawableButton может отображать не только готовые изображения, но и различные графические примитивы. Пример создания сложных кнопок DrawableButton в виде звезд с произвольным числом лучей с использованием класса DrawablePath входит в поставку Juce (см. <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp).
В данном примере DrawableButton легко можно заменить на ShapeButton — кнопку, содержащую залитую геометрическую фигуру (shape). Установить эту фигуру можно с помощью метода void ShapeButton::setShape(const Path newShape, bool resizeNowToFitThisShape, bool maintainShapeProportions, bool hasDropShadow), где newShape — собственно, рисуемая фигура (объект класса Path), а maintainShapeProportions и hasDropShadow — флаги того, должны ли размеры кнопки подстраиваться под размеры фигуры, и того, должна ли фигура отбрасывать тень, соответственно.
Мы не будем подробно останавливаться на работе с , поскольку она сходна с таковой для DrawableButton, разница лишь в использовании объектов класса Image, а не Drawable в методе setImages плюс задание цветов кнопки при манипуляции с ней мышью (наведение и т.п.). Пример использования имеется в демонстрационном приложении Juce (см. <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp).
ToolbarItcodeComponent — кнопка инструментальной панели — подробно рассмотрена в лекции 15 (комоненты панели инструментов).
HyperlinkButton — кнопка, отображающая подчеркнутую гиперссылку на ресурс Web, который запускается в браузере, установленном по умолчанию, при щелчке по ней. Особенностью кнопки является то, что для ее работы не нужен слушатель (ButtonListener), хотя его установка и возможна, например, если вы хотите, чтобы были выполнены какие-либо действия перед переходом по ссылке.
Конструктор класса кнопки с гипертекстовой ссылкой HyperlinkButton::HyperlinkButton(const String linkText, const URL linkURL) принимает два параметра: linkText — отображаемый текст и linkURL — собственно гиперссылка, объект специального класса Juce, URL.
Ссылку на ресурс Web можно изменить с помощью метода void HyperlinkButton::setURL(const URL newURL ) throw().
Как и у любой кнопки, текст HyperlinkButton можно изменить методом setButtonText.
Пример использования HyperlinkButton представлен в листинге 12.6, внешний вид работающей программы показан на рисунке12.6
#include "TCentralComponent.h"
//---------------------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//---------------------------------------------------------------------
TCentralComponent::TCentralComponent() : Component("Central Component"),
pHyperlinkButton(0)
{
// Кнопка с гиперссылкой
pHyperlinkButton =
new HyperlinkButton(L"http://www.rawmaterialsoftware.com/juce.php",
URL(L"http://www.rawmaterialsoftware.com/juce.php"));
JuceHyperlinkButton // Устанавливаем текст всплывающей подсказки
pHyperlinkButton->setTooltip(
L"http://www.rawmaterialsoftware.com/juce.php");
addAndMakeVisible(pHyperlinkButton);
setSize(400, 200);
}
//---------------------------------------------------------------------
TCentralComponent::~TCentralComponent()
{
// Удаляем дочерний виджет
// и обнуляем его указатель
deleteAndZero(pHyperlinkButton);
}
//---------------------------------------------------------------------
void TCentralComponent::paint(Graphics Canvas)
{
Canvas.fillAll(Colours::white);
}
//---------------------------------------------------------------------
void TCentralComponent::resized()
{
pHyperlinkButton->setBounds(15, proportionOfHeight(0.4500f),
getWidth() - 15, 25);
}
//---------------------------------------------------------------------
(рис 12.6) Внешний вид программы с HyperlinkButton
В данной лекции нами рассмотрены классы, необходимые для создания кнопок различного вида: кнопок с текстом TextButton, кнопок со стрелкой ArrowButton, кнопок с изображениями (, DrawableButton и ShapeButton) и гиперссылки (HyperlinkButton). Базовым классом для всех кнопок является Button.
Напишите игру "Светофор": при нажатии на одну из трех кнопок, расположенных вертикально, она окрашивается в соответствующий цвет (красный, желтый или зеленый), а остальные — в темно-серый.
Архив с исходными текстами примеров Вы можете скачать здесь
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.