メインコンテンツまでスキップ

チュートリアルリスナーとブロードキャスター

このチュートリアルでは、JUCEの重要なコンセプトであるリスナーとブロードキャスターのシステムを紹介します。ボタンのクリックに応答する簡単なアクションの実装を通して、この概念を見ていきます。

レベル:初級

プラットフォーム:Windows, macOS, Linux, iOS, Android

クラス: ボタン, テキストボタン, ボタン::リスナー, 時間

はじめる

Download the demo project for this tutorial here: ピップ | ジップ. Unzip the project and open the first header file in the Projucer.

If you need help with this step, see チュートリアルProjucerパート1:Projucerを始める.

The demo project

このチュートリアルのデモ・プロジェクトでは、1つのボタンと1つのラベルを持つシンプルなユーザー・インターフェースを紹介します。インターフェイスは次のスクリーンショットに似ているはずです:

A basic button and label interface.
A basic button and label interface.

インターフェイスは提供された状態では何もしない。ボタンをクリックすると、ラベルに現在の日付と時刻が表示されるようにコードを追加する。

Configuring the interface

The メインコンテンツコンポーネント class comprises two child components: a テキストボタン object and a ラベル object. A テキストボタン object can display a button containing some specific text, and a ラベル object can display a piece of text.

注記

The テキストボタン class implements one type of button. There are many types of ボタン class available in JUCE. See the API reference documentation for the トグルボタン, シェイプボタン, イメージボタン, 描画可能ボタン, and 矢印ボタン classes for more information.

The declaration for the メインコンテンツコンポーネント class is as follows:

class MainContentComponent : public juce::Component
{
を公開します:
//==============================================================================
MainContentComponent()
{
// ...
}

~メイン・コンテンツ・コンポーネント()
{
// ...
}

void resized() オーバーライド
{
// ...
}

private:
juce::TextButton checkTheTimeButton;
juce::Label timeLabel;

//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)this is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and a JUCE_LEAK_DETECTOR macro for ...definition juce_PlatformDefs.h:245

The button and the label are added to the メインコンテンツコンポーネント object and made visible in the メインコンテンツコンポーネント constructor:

MainContentComponent()
{
addAndMakeVisible (checkTheTimeButton);
checkTheTimeButton.setButtonText ("Check the time...");

addAndMakeVisible (timeLabel);
timeLabel.setColour (juce::Label::backgroundColourId, juce::Colours::black);
timeLabel.setColour (juce::Label::textColourId, juce::Colours::white);
timeLabel.setJustificationType (juce::Justification::centred);

setSize (600, 110);
}

ここでは、ボタンのテキストを設定し、ラベルの特定の外観を設定します。これにより、ラベルは黒い背景に白いテキストを表示します。デフォルトでは、ラベルはテキストを表示しません。

Adding a listener base class

In JUCE, buttons, sliders, and many other types of controls that may need to inform other objects about a change in their state are a type of ブロードキャスター object. In order to respond to changes in a broadcaster object, other classes need to be a リスナー for that specific type of broadcaster. Listeners also need to be registered with at least one specific broadcaster object of that type. (The broadcaster-listener system in JUCE follows the オブザーバーパターン.) Many broadcaster objects contain a nested リスナー class, from which we can inherit, in order to become a listener for that type of broadcaster. For example, the ボタン class contains a nested class ボタン::リスナー for this purpose.

注記

The ボタン::リスナー class can be used to listen to any of the different button types including instances of the テキストボタン class as shown here.

To use the ボタン::リスナー class we need to add it as a base class. In our case, we need to add the ボタン::リスナー class as a base class of the メインコンテンツコンポーネント class [1]:

class MainContentComponent : public juce::Component、
public juce::Button::Listener // [1].
{
public:

カスタム・クラスは、同じようにリスナー・ベース・クラスを追加することで、異なるタイプのブロードキャスターのリスナーになることができる。

Declaring the listener's callback

Usually each listener class has at least one ピュアバーチャル function. This is the function that will be called as a callback when the broadcaster object needs to broadcast its change. We must override this in order for the code to compile, and for us to use it.

注記

Listener classes often contain other virtual functions that may be overridden, but these are optional as they are needed in fewer cases. See the documentation for the スライダー::リスナー class for an example.

The ピュアバーチャル function in the ボタン::リスナー class is the ボタン::リスナー::buttonClicked() function. We need to add its declaration [2] within our メインコンテンツコンポーネント class as shown here:

MainContentComponent()
{
// ...
}

~メイン・コンテンツ・コンポーネント()
{
// ...
}

void resized() オーバーライド
{
// ...
}

void buttonClicked (juce::Button* button) override // [2].
{
}

// ...
buttonfloat float UnityEventModifiers int buttonDefinition juce_UnityPluginInterface.h:191

Implementing the listener callback

Now, let's implement the MainContentComponent::buttonClicked() function. Here, we are passed a pointer to the object that has broadcasted the change. We can then compare this pointer with other objects to determine which object it was:

void buttonClicked (juce::Button* button) override // [2].
{
if (button == &checkTheTimeButton) // [3].
{
auto currentTime = juce::Time::getCurrentTime(); // [4] { // [3] { auto currentTime = juce::Time::getCurrentTime(); // [4

auto includeDate = true;
auto includeTime = true;
auto currentTimeString = currentTime.toString (includeDate, includeTime); // [5].

timeLabel.setText (currentTimeString, juce::dontSendNotification); // [6].

}
  • [3]: Here we compare the pointer passed to the function with the address of our button, to see if it matches. You should do this even if you have only one button, as we do here. It is safe to compare the pointer to the base ボタン class passed to the function with instances of ボタン subclasses, such as the テキストボタン class as shown here.
  • [4]: This uses the 時間 class to get the current time from the operating system.
  • [5]: This converts the 時間 object to a readable string. The two ブール values allow some customisation of the output (see the documentation for the Time::toString() function for more information).
  • [6]: Here we update the text displayed within the label.
注記

The 通知を送信しない argument [7] prevents the label from broadcasting this change to its listeners, should it have any. (ラベル objects can have listeners since they can be used to edit text, too.) In this case we know that it can't have any listeners (as it is our own private member) but it is good practice to be specific.

Registering as a listener with the broadcaster

In order to receive the messages that are broadcast, we need to register our listener object with one or more broadcaster objects. In this case, we need to register with the テキストボタン object. Typically, this would be done within the constructor of the listener subclass [7]:

MainContentComponent()
{
addAndMakeVisible (checkTheTimeButton);
checkTheTimeButton.setButtonText ("Check the time...");
checkTheTimeButton.addListener (this); // [7].
注記

Most broadcaster objects have an addListener() function for this purpose (the チェンジブロードキャスター object is an exception, it has the ChangeBroadcaster::addChangeListener() function instead).

Deregistering as a listener with the broadcaster

Broadcasters will also have a removeListener() function, too. For example, see the ボタン::removeListener() function. Since our button is owned by the same class that is performing the listening we don't really 必要 to remove the listener as the button will be destroyed at the same time as the listener. For completeness, we 可能性がある add this to our destructor:

~メインコンテンツコンポーネント()
{
checkTheTimeButton.removeListener (this);
}
警告

リスナーを適切に削除することは、より複雑なブロードキャスターとリスナーのシステムをセットアップする場合に重要である。

アプリケーションをビルドして実行します。ボタンをクリックすると、ラベルに時間が表示されるはずです。

Using the button to display the current time.
Using the button to display the current time.
注記

The completed code for this section can be found in the ListenersAndBroadcastersTutorial_02.h file of the demo project for this tutorial.

Try changing the format of the text displayed. You can do this by changing the arguments passed to the Time::toString() function. You could also change the code such that the label displays the number of milliseconds between button clicks, rather than the absolute time.

注記

An example implementation of the code for this exercise can be found in the ListenersAndBroadcastersTutorial_03.h file of the demo project for this tutorial.

Simplifying the Button callback

このチュートリアルで示したようなリスナーやブロードキャスターのパラダイムを使う代わりに、最新のC++標準のラムダ関数を使うことで、ボタンのコールバックを単純化することができます。これは、複雑な実装を必要としない単純なコールバックには特に有効です。

First, let's remove the inheritance from the ボタン::リスナー class and restore the MainContentComponent class definition like this:

class MainContentComponent : public juce::Component
{
public:

Then, instead of adding the MainContentComponent as a listener to the ボタン, assign a lambda function to the ボタン::onClick helper object [8] as follows:

MainContentComponent()
{
addAndMakeVisible (checkTheTimeButton);
checkTheTimeButton.setButtonText ("Check the time...");
checkTheTimeButton.onClick = [this] { checkTime(); }; // [8].

This tells the ボタン object which function to call when the ボタン is clicked by the user.

Finally, rename the callback function to checkTime() [9] and remove the if() statement checking the pointers to the ボタン objects as we don't need to check which ボタン called the function anymore:

void checkTime() // [9].
{
auto currentTime = juce::Time::getCurrentTime();

auto includeDate = true;
auto includeTime = true;
auto currentTimeString = currentTime.toString (includeDate, includeTime);

timeLabel.setText (currentTimeString, juce::dontSendNotification);
}
注記

The implementation of the code can be found in the ListenersAndBroadcastersTutorial_04.h file of the demo project for this tutorial.

概要

このチュートリアルでは、JUCEにおけるブロードキャスターとリスナーのシステムの基本を紹介しました。このチュートリアルではボタンに焦点を当てましたが、同じテクニックはJUCEコードの多くの領域に適用できます。特に、以下のことを学びました:

  • カスタム・クラスの1つをリスナー・タイプのオブジェクトにする方法。
  • リスナーのコールバック関数の追加方法
  • ブロードキャスター・オブジェクトのリスナーとしての登録と登録解除の方法。
  • How to access the current time using the 時間 class.
  • ラムダ関数でコールバックを単純化する方法。

関連項目