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

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

📚 Source Page

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

レベル: 初級
プラットフォーム: Windows, macOS, Linux, iOS, Android
クラス: Button, TextButton, Button::Listener, Time

はじめに

このチュートリアルのデモプロジェクトをこちらからダウンロードしてください:PIP | ZIP。プロジェクトを解凍し、最初のヘッダファイルを Projucer で開いてください。

この手順でサポートが必要な場合は、チュートリアル:Projucer Part 1: Projucer を始めようを参照してください。

デモプロジェクト

このチュートリアルのデモプロジェクトは、1 つのボタンと 1 つのラベルを持つシンプルなユーザーインターフェースを表示します。インターフェースは以下のスクリーンショットのようになるはずです:

基本的なボタンとラベルのインターフェース
基本的なボタンとラベルのインターフェース

提供された状態ではインターフェースは何もしません。ボタンをクリックするとラベルに現在の日時を表示するコードを追加します。

インターフェースの設定

MainContentComponentクラスは 2 つの子コンポーネントで構成されています:TextButtonオブジェクトとLabelオブジェクトです。TextButtonオブジェクトは特定のテキストを含むボタンを表示でき、Labelオブジェクトはテキストを表示できます。

ヒント

TextButtonクラスはボタンの 1 つのタイプを実装しています。JUCE には多くのタイプのButtonクラスがあります。詳細については、ToggleButtonShapeButtonImageButtonDrawableButtonArrowButtonクラスの API リファレンスドキュメントを参照してください。

MainContentComponentクラスの宣言は以下のとおりです:

class MainContentComponent : public juce::Component
{
public:
//==============================================================================
MainContentComponent()
{
// ...
}

~MainContentComponent()
{
// ...
}

void resized() override
{
// ...
}

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

//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

ボタンとラベルはMainContentComponentオブジェクトに追加され、MainContentComponentコンストラクタで可視化されます:

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);
}

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

リスナー基底クラスの追加

JUCE では、ボタン、スライダー、および状態の変化を他のオブジェクトに通知する必要がある他の多くのタイプのコントロールは、ブロードキャスターオブジェクトの一種です。ブロードキャスターオブジェクトの変化に応答するには、他のクラスはその特定のタイプのブロードキャスターのリスナーである必要があります。リスナーはまた、そのタイプの少なくとも 1 つの特定のブロードキャスターオブジェクトに登録する必要があります。(JUCE のブロードキャスター-リスナーシステムはオブザーバーパターンに従います。)多くのブロードキャスターオブジェクトには、そのタイプのブロードキャスターのリスナーになるために継承できるネストされたListenerクラスが含まれています。例えば、Buttonクラスには、この目的のためにネストされたクラスButton::Listenerが含まれています。

ヒント

Button::Listenerクラスは、ここに示されているTextButtonクラスのインスタンスを含む、異なるボタンタイプのいずれかをリッスンするために使用できます。

Button::Listenerクラスを使用するには、基底クラスとして追加する必要があります。私たちの場合、Button::ListenerクラスをMainContentComponentクラスの基底クラスとして追加する必要があります[1]:

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

カスタムクラスは、同様に複数のリスナー基底クラスを追加することで、異なるタイプのブロードキャスターのリスナーになることができます。

リスナーのコールバックの宣言

通常、各リスナークラスには少なくとも 1 つの純粋仮想関数があります。これは、ブロードキャスターオブジェクトが変更をブロードキャストする必要があるときにコールバックとして呼び出される関数です。コードをコンパイルし、使用するためにはこれをオーバーライドする必要があります。

ヒント

リスナークラスには、オーバーライドできる他の仮想関数が含まれていることが多いですが、これらは少数のケースで必要とされるため、オプションです。例としてSlider::Listenerクラスのドキュメントを参照してください。

Button::Listenerクラスの純粋仮想関数はButton::Listener::buttonClicked()関数です。ここに示すように、MainContentComponentクラス内にその宣言[2]を追加する必要があります:

MainContentComponent()
{
// ...
}

~MainContentComponent()
{
// ...
}

void resized() override
{
// ...
}

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

// ...

リスナーコールバックの実装

では、MainContentComponent::buttonClicked()関数を実装しましょう。ここでは、変更をブロードキャストしたオブジェクトへのポインタが渡されます。このポインタを他のオブジェクトと比較して、どのオブジェクトかを判断できます:

void buttonClicked (juce::Button* button) override // [2]
{
if (button == &checkTheTimeButton) // [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]:ここでは、関数に渡されたポインタをボタンのアドレスと比較して、一致するかどうかを確認します。ここのように 1 つしかボタンがなくても、これを行うべきです。関数に渡された基底Buttonクラスへのポインタを、ここに示されているTextButtonクラスなどのButtonサブクラスのインスタンスと比較するのは安全です。
  • [4]:これはTimeクラスを使用してオペレーティングシステムから現在の時刻を取得します。
  • [5]:これはTimeオブジェクトを読みやすい文字列に変換します。2 つのbool値により、出力のカスタマイズが可能です(詳細はTime::toString()関数のドキュメントを参照)。
  • [6]:ここではラベル内に表示されるテキストを更新します。
ヒント

dontSendNotification引数[7]は、ラベルがリスナーを持っている場合、この変更をリスナーにブロードキャストすることを防ぎます。(Labelオブジェクトはテキストの編集にも使用できるため、リスナーを持つことができます。)この場合、リスナーを持つことができないことはわかっています(自分のプライベートメンバーなので)が、明示的にすることは良い習慣です。

ブロードキャスターへのリスナーとしての登録

ブロードキャストされるメッセージを受信するには、1 つ以上のブロードキャスターオブジェクトにリスナーオブジェクトを登録する必要があります。この場合、TextButtonオブジェクトに登録する必要があります。通常、これはリスナーサブクラスのコンストラクタ内で行われます[7]:

MainContentComponent()
{
addAndMakeVisible (checkTheTimeButton);
checkTheTimeButton.setButtonText ("Check the time...");
checkTheTimeButton.addListener (this); // [7]
ヒント

ほとんどのブロードキャスターオブジェクトには、この目的のためのaddListener()関数があります(ChangeBroadcasterオブジェクトは例外で、代わりにChangeBroadcaster::addChangeListener()関数があります)。

ブロードキャスターへのリスナー登録解除

ブロードキャスターにはremoveListener()関数もあります。例えば、Button::removeListener()関数を参照してください。ボタンはリスニングを行っているクラスと同じクラスに所有されているため、ボタンはリスナーと同時に破棄されるため、リスナーを削除する必要は実際にはありません。完全性のために、デストラクタにこれを追加できます

~MainContentComponent()
{
checkTheTimeButton.removeListener (this);
}
警告

より複雑なブロードキャスター-リスナーシステムを設定する場合、リスナーを適切に削除することは重要です。

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

ボタンを使用して現在の時刻を表示
ボタンを使用して現在の時刻を表示
ヒント

このセクションの完成したコードは、このチュートリアルのデモプロジェクトのListenersAndBroadcastersTutorial_02.hファイルにあります。

注記

演習:表示されるテキストの形式を変更してみてください。Time::toString()関数に渡す引数を変更することで行えます。また、絶対時間ではなく、ボタンクリック間のミリ秒数を表示するようにコードを変更することもできます。

ヒント

この演習のコードの例は、このチュートリアルのデモプロジェクトのListenersAndBroadcastersTutorial_03.hファイルにあります。

Button コールバックの簡素化

このチュートリアルで示されているリスナーとブロードキャスターのパラダイムを使用する代わりに、最新の C++標準のラムダ関数を使用してボタンコールバックを簡素化できます。これは、複雑な実装を必要としないシンプルなコールバックに特にうまく機能します。

まず、Button::Listenerクラスからの継承を削除し、MainContentComponent クラスの定義を以下のように復元します:

class MainContentComponent : public juce::Component
{
public:

次に、MainContentComponent をButtonのリスナーとして追加する代わりに、Button::onClickヘルパーオブジェクトにラムダ関数を割り当てます[8]:

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

これは、ユーザーがButtonをクリックしたときに呼び出す関数をButtonオブジェクトに伝えます。

最後に、コールバック関数の名前を checkTime()[9]に変更し、どのButtonが関数を呼び出したかをチェックする必要がなくなったので、Buttonオブジェクトへのポインタをチェックする if()文を削除します:

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);
}
ヒント

このコードの実装は、このチュートリアルのデモプロジェクトのListenersAndBroadcastersTutorial_04.hファイルにあります。

まとめ

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

  • カスタムクラスの 1 つをリスナータイプのオブジェクトにする方法。
  • リスナーコールバック関数を追加する方法。
  • ブロードキャスターオブジェクトへのリスナーとしての登録と登録解除の方法。
  • Timeクラスを使用して現在の時刻にアクセスする方法。
  • ラムダ関数でコールバックを簡素化する方法。

関連項目