Android App 〜画面作成を行う〜

画面作成

作成したのは、以下の機能です。

  1. 多言語化
  2. 画面に文字列を表示する

stringc.xmlを使う

下のように作成した*strings.xmlの中にある「android\:id="@+id\/top_title"」をアプリに反映するというところです。
次のファイルに以下の1行を追加しました。

android:id="@+id/top_title"

全体を記述すると下のようになります。

<activity_main.xml>

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/top_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/app_name"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.534"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.05" />

</androidx.constraintlayout.widget.ConstraintLayout>

<strings.xml>

<resources>
    <string name="app_name">目標達成アプリ</string>
</resources>

これを実行した結果、下のように表示されました。

テキストのサイズ変更

したのようなプロパティ(属性)を設定することで、テキストのサイズを変更できました。

        android:layout_width="match_parent"
        android:layout_height="100dp"

そして、自動サイズ調整はしたのようにやるみたいです。

        android:autoSizeTextType="uniform"
        android:autoSizeMinTextSize="12sp"
        android:autoSizeMaxTextSize="100sp"

これを記述するとAndroid APIのレベル別のactivity_main.xmlが作成されるようです。
※Android Studioで「Override XXX」と表示されるのでそれをクリックしました。

最終的に、下のようなファイルが作成されました。

イメージの追加

説明を文言でするよりも、動画の方がよいと思いました。

サポートライブラリ追加

ここで、エラーが発生しています。これは、AppCompatというサポートライブラリが無いために起きているエラーです。
これをインストールするためには、Java11が必要になります。

JDK11インストール

WidnowsでのAndroid Studioが起動できなかったので、現在はMacで作業をしていますので、home brewを使用してインストールしました。参考サイトはこちらです。

下のコマンドでインストールできました。

brew install java11

しかし、次のようなエラーが出ました。

Invalid Gradle JDK configuration found

これは、プロジェクト構成(Project Structure) -> SDKLocation -> JDKの設定でJava11tを設定し、改めてビルドしたらなおりました。

Android Studioアップグレード

そして、初めのイメージを設定する部分ですが、次のようなエラーが出ました

Sets a drawable as the content of this ImageView. Allows the use of vector drawable when running on older versions of the platform.

これは、Android Studioをアップデートしてください。というものでした。。。

こちらのサイトを参考にAndroid Studioは下のようにConfigure -> Check for Updateを選択します。

しかし、選択肢が「Download」しかなかったので、結局新しいものをダウンロード、インストールすることになりました。
最終的に置き換えるという形で、アップグレードしました。

まだ、エラーが解消されません。。。

This view is not constrained. It only has designtime positions, so it will jump to (0,0) at runtime unless you add the constraints

ここのサイトを参考にすると設定を追加すると治るということなので、次のようにプロパティを追加しました。

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/ic_add_mokuhyo"
        tools:layout_editor_absoluteX="50dp"
        tools:layout_editor_absoluteY="222dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="VectorDrawableCompat" />

追加したのは、次の部分です。

        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"

そして、英語、日本語と多言語化した場合は、下の3つのファイルに文字列を追加する必要がありました。

  • strings.xml(default)
  • strings.xml(ja)
  • strings.xml(en)

イメージのレイアウト

結構手こずりました。下のサイトを参考に学習しました。

  1. Image Asset Studioを使用する

色々と試したけど結局実行するデバイスを>色々と試したけど結局実行するデバイスを 新しくして試すことにしました。。。

どうやらしているする属性が違うようでした。こちらのサイトでありました。

 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <ImageView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:src="@drawable/my_image"
         android:contentDescription="@string/my_image_description"
         />
 </LinearLayout>
 android:src="@drawable/my_image"

この部分が自動生成したものと値が違う。。。

そして、レイアウトの設定も問題がありそうなので、本家のサイトを参考にレイアウトも学習します。

何かしら触っていると、わかってくるような感じで説明がうまくできないのですが、 各値を設定してやると、見た目も変更されるのでそれで、自分の思った通りに修正するのが、早いと思います。

画像が表示されいない問題

これの原因がわかりました。SDKのバージョン別にactivity_main.xmlが存在していました。

これが原因で、一向に画像が表示されなかった。。。というわけでした。。。

最終的に作成したactivity_main.xmlは下のようなものです。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TableRow
            android:layout_width="178dp"
            android:layout_height="192dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <ImageView
                    android:id="@+id/imageView3"
                    android:layout_width="80dp"
                    android:layout_height="80dp"
                    android:layout_marginStart="40dp"
                    android:layout_marginTop="10dp"
                    android:contentDescription="目標追加・一覧"
                    android:src="@drawable/ic_add_mokuhyo"
                    android:visibility="visible"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent"
                    tools:srcCompat="@drawable/ic_add_mokuhyo" />

                <TextView
                    android:id="@+id/textAddMokuhyo"
                    android:layout_width="180dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="10dp"
                    android:layout_marginTop="10dp"
                    android:autoSizeMaxTextSize="40dp"
                    android:autoSizeMinTextSize="18dp"
                    android:text="目標追加・一覧"
                    android:textSize="20dp"
                    android:visibility="visible"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/imageView3" />
            </LinearLayout>

        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"></LinearLayout>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"></LinearLayout>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"></LinearLayout>
        </TableRow>

    </TableLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

LinerLayoutを学ぶ

よく使用されるであろうレイアウトの1つとして「LinerLayout」があります。ドキュメントにも載っているので基本的なレイアウトなのであろうと思いこれを学ぶことにしました。

LinearLayout は、すべての子を垂直または水平の一方向に揃えるビューグループです。android\:orientation 属性でレイアウトの方向を指定できます。

とりあえずは、コンポーネント(ボタンなどの部品)を並べてみます。そして、プロパティ(android\:XXXX)の値を変えてどのような配置になるかみてみたいと思います。

ここで、着目するプロパティを以下に示します。

プロパティ名 内容 
android\:orientation レイアウトの方向を指定
android\:layout_weight 画面上で占めるスペースという観点で「重要度」を表す値をビューに指定します。この weight 値が大きいほど、親ビューの残りのスペースを埋めるように拡大されます。子ビューに weight 値を指定することで、ビューグループの残りのスペースを、宣言された weight の割合に応じて割り当てることができます。デフォルトの weight 値は 0 です。
android\:layout_height
android
:layout_width
android\:layout_weight
それぞれの値を0dp、0dp、1に設定することで均等配分することができます。

ちょっと試してみましたが、他のプリパティと組み合わせる必要があるので、色々やってみないと理解できません。

次は、合わせて出てきた、TextViewに関してもプロパティを見てみます。

感想

  1. レイアウトの使い方、センタリングなど、やり方を調べると色々出てくるので手が止まることは少なくなりそうだ。
  2. レイアウトマネージャー(動画にある画面)での操作はわかりやすいが、プロパティの場所を探すのが大変だった。

でわでわ。。。

UMLの書き方読み方~シーケンス図~

シーケンス図の書き方

IT専科というサイトを参考にするとシーケンス図とは。。。

シーケンス図とは、クラスやオブジェクト間のやりとりを時間軸に沿って表現する図です。

アジャイルモデリング(AM)というサイトを参考にして学習します。そして、下のような図があります。

シーケンス図1

これは、手書きなのでちょっとわかりずらいですが、ポイントとしては、次のような部分です。

  1. 登場人物が3人(アプリケーションを含む)

  2. それぞれの時間軸が縦に伸びている

  3. 縦軸に対して、横軸は「何かしらの動きを示す」

大まかに、このようなところがポイントになります。
アジャイルモデリング(AM)というサイトには、下のような説明がありました。

これがシーケンス図と呼ばれている理由は明らかでしょう。ロジックの実行順序(シーケンス)がメッセージ(横向きの矢印)の順序で示されています。最初のメッセージは左上から始まり、次のメッセージはそのすぐ下に書く、というように表していきます。

シーケンス図の内訳

サービスレベルのシーケンス図

参考サイトには、下のような説明があります。

図の上部に横に並んでいる箱は、分類子またはそのインスタンスを表します。この分類子は通常、ユースケース、オブジェクト、クラス、またはアクターです。オブジェクトとクラスにはメッセージを送ることができるため(オブジェクトは操作の呼び出しを通じて、クラスは静的操作の呼び出しを通じてメッセージに応答します)、これらをシーケンス図に含めるのは筋が通っています。アクターも、利用シナリオを開始したり、利用シナリオで能動的な役割を果たすので、シーケンス図に含めることができます。オブジェクトにはUML標準の「名前: クラス名」という書式でラベルをつけます。この名前は必須ではありません(図で名前の付いていないオブジェクトのことを無名オブジェクトと呼びます)。クラスには「クラス名」という書式でラベルを付け、アクターには「アクター名」の書式で名前を付けます。オブジェクトのラベルには下線が引かれていますが、クラスとアクターには引かれていないことに注意してください。たとえば、図3の学生オブジェクトにはある学生という名前が付けられています。これが名前付きオブジェクトです。それに対してゼミのインスタンスは無名オブジェクトです。Studentのインスタンスに名前が付けられているのは、複数の場所でメッセージのパラメータとして使われているためです。ゼミのインスタンスの方は、図の他の場所で参照する必要がないので、無名にしておくことができます。図2では、学生クラスが永続性フレームワーククラスにメッセージを送っています(永続性フレームワーククラスには\<\>というステレオタイプを付けてもよかったのですが、図を簡潔にしておくために付けませんでした)。クラスに対して送られたメッセージは、すべて静的メソッドとして実装します。これについては後で説明します。

まとめると次のようになります。
上の図で示した「登場人物」は、「ユースケース、オブジェクト、クラス、またはアクター」

  • ユースケース:「申込用紙を書く」とか、「送信ボタンを押下」などのように人の動き、作業を示す。
  • オブジェクト:クラスとかインスタンスのこと(厳密にはインスタンスの事)
  • アクター  ;人のモデル(人を示す絵)

そして、オブジェクトはメッセージを送信することができるので、ほかのオブジェクトを呼び出し、何かしらの処理を行わせることができます。その処理が終わったら、また元のオブジェクトの線に戻ってきます。下のような矢印のことです。

※引用した文言の中に「すべて静的メソッドとして実装します」とありますが、これはこちらのサイトでそのように実装しているということです。別に静的メソッドである必要はありません

そして、上記の手書きの画像をきれいに書くと下のようになるようです。

ここで、シーケンス図に使用される図をまとめると、IT専科というサイトの表を借りると下のようになります。

構成要素一覧
要素 表示形式 意味
ライフライン(Lifeline) ライフライン 記号 使用するオブジェクトやクラスを表現します。どちらか一方なら省略可能です。
実行仕様(ExecutionSpecification) 実行仕様 記号 生成されているライフラインが実行状態であることを意味します。
停止(Stop) 停止 記号 生成されたライフライン自体の消滅を意味します。
メッセージ(Message) 同期(Synchronous)メッセージ 同期メッセージ 記号 送り先のライフラインの実行に同期されるメッセージを意味します。メッセージ名には具体的な関数やINCLUDEディレクティブ等を記入します。
非同期(Asynchronous)メッセージ 非同期メッセージ 記号 送り先のライフラインの実行に同期されないメッセージを意味します。メッセージ名には具体的な関数やINCLUDEディレクティブ等を記入します。
応答(Reply)メッセージ 応答メッセージ 記号 送り先のライフラインから送り手への戻り値を意味します。メッセージ名には戻り値を格納する具体的な変数名等を記入します。
ファウンド(Found)メッセージ ファウンドメッセージ 記号 図解上にない送り手から送られた、もしくは送り手がダイアグラム上にないことを意味します。
ロスト(Lost)メッセージ ロストメッセージ 記号 意図された受け手に送られていない、もしくは受け手がダイアグラム上にないことを意味します。

▲PageTop

制御構造の記述

シーケンス図では、制御構造を表現するために「複合フラグメント」を使用します。種類および、記述例は次の通りです。

複合フラグメントの種類

複合フラグメントには、次の種類があります。

複合フラグメント一覧
InteractionOperator 読み 意味
ref 相互作用使用(InteractionUse) 別のシーケンス図を参照することを表します。
alt オルタナティブ(Alternative) 分岐処理を表します。
opt オプション(Option)

条件を満たした場合のみ実行される処理を表します。

par パラレル(Parallel) 並列処理を表します。
loop ループ(Loop) ループ(繰り返し)処理を表します。
break ブレイク(Break) 処理の中断を表します。
critical クリティカル(Critical) マルチスレッド環境での同期処理など、排他制御を表します。
assert アサーション(Assert) 処理が妥当であるための定義を表します。
neg 否定(negation)

本来、実行されるはずがない処理(メッセージ)であることを表します。

ignore 無効(ignore) あまり重要な処理(メッセージ)ではないことを表します。
consider 有効(Consider) 重要な処理(メッセージ)であることを表します。

基本的な処理を表現する

ここでいう「基本的な処理」とは、次のものを指します。

  1. 参照(REF)
  2. 条件分岐(ALT)
  3. 条件判断(OPT)
  4. 並列処理(PAR)
  5. 反復処理(LOOP
  6. 中断(BREAK
  7. クリティカルセッション(CRITICAL
  8. アサート(ASSERT
  9. 不正なシーケンス(NEG
  10. 無効(IGNORE
  11. 有効(CONSIDER

このような形で記述します。あとは、どのような動きを表現したいのか?を考えるだけです。
しかし、これらの「動き」を考えるためには、プログラミングの基礎を理解する必要があります。
※よかったら参考にどうぞ、Java Basic学習フロー

具体的には、「じゃんけんゲーム」を作成しようと考えたときには、どのような画面で、ユーザーの入力はどのように行うのか?などの「人間レベル」の動きから、「入力値からどのような処理をして勝敗の判定を行うか?」という「プログラムレベル」の動きを考える必要があるためです。

こんなところで失礼します。

でわでわ。。。

Android App 作成 ~アプリの基礎を学ぶ~

Androidアプリの基礎

下のページにあるチュートリアルを行っていきます。
参考サイト:Android Developperのサイトです。

そして、Androidアプリを作成するうえで理解しておきたいのが、下のようなライフサイクルと呼ばれるものです。
LifeCycle

具体的には、「アクティビティのライフサイクル」ということです。

「初めてのアプリを作成する」という項目は飛ばします。以前やったので。。。

概要をつかむ

Androidアプリを作成するための材料(コンポーネント)として以下のものがあります。

  • アクティビティ
  • サービス
  • ブロードキャスト レシーバ
  • コンテンツ プロバイダ

つまりは、上のような「グループに分けられたクラスがありますよ」ということです。

これらのクラスがそれぞれ、次のように説明されています。

アクティビティ
アクティビティは、ユーザーとやり取りするためのエントリ ポイントです。これは、1 つのユーザー インターフェースを持つ 1 つの画面で表されます。たとえば、メールアプリには、新着メールの一覧を表示するアクティビティ、メールを作成するアクティビティ、そしてメールを閲覧するアクティビティがあります。メールアプリでは、これらの複数のアクティビティが一体となって 1 つのユーザー エクスペリエンスを形成しますが、それぞれのアクティビティは他のものから独立しています。したがって、これらのアクティビティのいずれかを、別のアプリから開始することができます(メールアプリが許可している場合)。たとえば、カメラアプリからメールアプリの新規メールを作成するアクティビティを開始できます。そのようにして、ユーザーが写真をメールで共有できるようにします。アクティビティは、システムとアプリ間における次の重要なインタラクションを行えるようにします。
アクティビティをホストしているプロセスを継続的に実行するために、ユーザーの現在の操作内容(画面の表示)を追跡。
以前に使用されたプロセス(停止されたアクティビティ)のうち、ユーザーが再度アクセスする可能性があるものを検知し、それらの優先順位を上げてプロセスを維持。
アプリがプロセスを強制終了した場合に、ユーザーが以前の状態を復元したアクティビティに戻れるように支援。
アプリ間でのユーザーフローをアプリが実装する手段と、システムがそれらのフローを連携させるための手段を提供(ここでは最も一般的な例を説明します)。
アクティビティは Activity クラスのサブクラスとして実装します。Activity クラスの詳細については、デベロッパー ガイドのアクティビティをご覧ください。

サービス
サービスは、さまざまな理由によりアプリをバックグラウンドで実行し続けるための汎用エントリ ポイントです。長期間の操作やリモート プロセスを処理するためにバックグラウンドで実行されるコンポーネントです。サービスにはユーザー インターフェースがありません。たとえば、サービスはユーザーが別のアプリを使用している間にバックグラウンドで音楽を再生したり、ユーザーが別のアクティビティを操作している間にそれを妨げることなくネットワークからデータを取得したりします。アクティビティなどの他のコンポーネントが、サービスを開始して実行したり、サービスとやり取りするためにサービスにバインドしたりすることができます。アプリの管理方法についてサービスがシステムに通知するセマンティクスは 2 つあり、それぞれ異なる意味を持っています。開始されたサービスが、作業が終了するまで自身の実行を維持するようシステムに指示します。これにより、ユーザーがアプリから離れても、バックグラウンドでデータを同期したり、音楽を再生したりできます。バックグラウンドでのデータの同期や音楽の再生は、開始されたサービスとしてはそれぞれ異なるタイプと認識され、システムがそれらのサービスに対して行う処理もそれぞれ異なります。
音楽の再生はユーザーが直接意識しているものなので、アプリはフォアグラウンドになりたいことをユーザーに通知で知らせることで、システムにそれを指示します。この場合、システムはそのサービス プロセスの実行を維持するよう最善を尽くします。このプロセスが終了するとユーザーが不満を覚えるからです。
通常のバックグラウンド サービスは、その実行をユーザーが直接意識していません。したがって、システムはより柔軟にそのプロセスを管理できます。ユーザーにとってより緊急な課題を処理するために RAM が必要となった場合には、プロセスを強制終了し、後から再開できます。
他のアプリ(またはシステム)がサービスを利用することを明示しているなら、バインドされたサービスが実行されます。これは基本的に、別のプロセスに API を提供するサービスです。これにより、これらのプロセス間に依存関係があることをシステムは認識します。プロセス A がプロセス B のサービスにバインドされている場合に、システムはプロセス B とそのサービスの実行を A のために維持する必要があることを認識します。さらに、プロセス A がユーザーにとって優先度が高い場合は、プロセス B もユーザーにとって重要であるとみなされます。サービスは良くも悪くもその柔軟性から、さまざまな上位レベルのシステム コンセプトにおいて、非常に有用な構成要素となってきました。ライブ壁紙、通知リスナー、スクリーン セーバー、入力方法、ユーザー補助機能サービス、その他多くの主要なシステム機能はすべて、アプリが実行するサービスとしてビルドされ、その実行時にシステムによりバインドされます。
サービスは Service のサブクラスとして実装されます。Service クラスの詳細については、デベロッパー ガイドのサービスをご覧ください。

ブロードキャスト レシーバ
ブロードキャスト レシーバは、通常のユーザーフローを外れて、システムがアプリにイベントを配信できるようにするコンポーネントです。これにより、アプリはシステム全体のブロードキャスト アナウンスに応答できます。ブロードキャスト レシーバは明確に定義されたアプリへのエントリであるため、システムは実行中でないアプリに対してもブロードキャストを配信できます。したがってアプリが、たとえば、近づいているイベントについてユーザーに知らせる通知を投稿するためのアラームをスケジューリングすることができます。アプリの BroadcastReceiver にアラームを配信することにより、そのアラームが作動するまでアプリの実行を維持する必要がなくなります。多くのブロードキャストの発信源はシステムです — たとえば、画面がオフになったことを通知するブロードキャスト、電池が残り少ないことを通知するブロードキャスト、画像がキャプチャされたことを通知するブロードキャストなどです。アプリから発信されるブロードキャストもあります — たとえば、端末にデータがダウンロードされ使用できることを他のアプリに知らせる場合などです。ブロードキャスト レシーバがユーザー インターフェースを表示することはありませんが、ステータスバー通知を作成して、ブロードキャスト イベントの発生時にユーザーにアラートできます。一般的には、ブロードキャスト レシーバは他のコンポーネントへの単なるゲートウェイであり、最小限の作業を行うことが前提となっています。たとえば、JobScheduler を使用してイベントに基づいた何らかの作業を実行する JobService をスケジュール設定する場合などです。
ブロードキャスト レシーバは BroadcastReceiver のサブクラスとして実装され、各ブロードキャストは Intent オブジェクトとして配信されます。詳細については、BroadcastReceiver クラスをご覧ください。

コンテンツプロバイダ
コンテンツ プロバイダは、ファイル システム、SQLite データベース、ウェブ、またはアプリからアクセス可能な他の永続的なストレージの場所に保存できるアプリデータの共有されている部分を管理します。コンテンツ プロバイダを介して、他のアプリがデータをクエリしたり、修正したりできます(コンテンツ プロバイダが許可している場合)。たとえば、Android システムはユーザーの連絡先情報を管理するコンテンツ プロバイダを提供しています。したがって、適切なパーミッションさえあれば、アプリからコンテンツ プロバイダに ContactsContract.Data などをクエリして、特定の人物に関する情報を読み取ったり書き込んだりできます。このような一般的なケースのために多くの API やサポートが組み込まれているため、コンテンツ プロバイダをデータベースの抽象化として考えたくなるかもしれません。しかし、システム設計の観点では、コンテンツ プロバイダには別の目的があります。システムから見て、コンテンツ プロバイダは、URI スキームにより識別される名前付きデータ項目を公開するための、アプリへのエントリ ポイントです。したがって、アプリは自身が保有するデータを URI 名前空間にどのようにマッピングし、その URI を他のエンティティに渡してデータにアクセスできるようにするかを決めることができます。アプリの管理に関して、システムでは以下が実行できます。
URI の割り当てはアプリが実行中であるかどうかには影響されないため、その URI を所有しているアプリが終了していても URI は維持されます。システムが確認する必要があるのは、該当 URI からアプリのデータを取得する必要があるときに、所有しているアプリが実行中であることだけです。
URI は重要で詳細なセキュリティ モデルも提供します。たとえば、アプリは画像の URI をクリップボードに配置することができますが、他のアプリが自由にアクセスできないようにコンテンツ プロバイダをロックしたままにできます。別のアプリがクリップボードのその URI にアクセスしようとした場合には、システムが一時的な URI パーミッションを付与して、その URI にあるデータのみにアクセスすることを許可します。その他のデータにはアクセスできません。
コンテンツ プロバイダは、アプリだけに公開されている、他で共有されていないデータを閲覧したり書き込んだりする場合にも役立ちます。
コンテンツ プロバイダは ContentProvider のサブクラスとして実装され、他のアプリがトランザクションを実行できるようにする API の標準セットを実装する必要があります。詳細については、デベロッパー ガイドのコンテンツ プロバイダをご覧ください。

これらを使って作る

上記のコンポーネント(部品)をしようしてAndroidアプリを作成します。この基礎になる部分をちゃんと理解すればほぼ極めたといってよいと思います。※勝手な想像です。なぜなら自分もこれから学習するので(笑)

なので、この基礎部分をしっかり学習したいと思います。

「概要をつかむ」のまとめ

Androidアプリを作るための基本的な材料(クラス)として次の4つがある。

  • アクティビティ
  • サービス
  • ブロードキャスト レシーバ
  • コンテンツ プロバイダ

そして、これらの材料を組み合わせてAndroidアプリを作成するということを理解。

次は、順番に「アクティビティ」を理解します。

アクティビティについて

アクティビティについて学習します。まずは、概要にある内容を抜粋を読みます。

main() メソッドで起動するアプリをプログラミングする際の枠組みとは異なり、Android システムでは、ライフサイクルのそれぞれの段階に対応するコールバック メソッドを呼び出すことにより、Activity インスタンス内のコードが開始されます。

つまるところは、以下の文言がキーポイントになります。

一般に、1 つのアクティビティがアプリ内の 1 つの画面を実装します。

例として、次のような説明があります。

アプリのアクティビティの 1 つが「設定」画面を実装し、他のアクティビティが「写真を選択」画面を実装します。

画面=アクティビティであり、このアクティビティの操作により様々な操作(処理)を実装するというわけです。

マニフェストで画面の設定

アプリでアクティビティを使用できるようにするには、マニフェストでアクティビティとその属性を宣言する必要があります。

    <manifest ... >
      <application ... >
          <activity android:name=".ExampleActivity" />
          ...
      </application ... >
      ...
    </manifest >

必須の属性は、アクティビティのクラス名を指定する android\:name だけです。

簡単ですね。つまりわかりやすい=使いやすいということだと思います。
続けて、そのほかの設定に関しては、次の部分にあります。

ラベル、アイコン、UI テーマなどのアクティビティの特性を定義する属性を追加することもできます。

こちらのページにアクティビティの詳細がありました。

構文は以下のようになっています。

<activity android:allowEmbedded=["true" | "false"]
          android:allowTaskReparenting=["true" | "false"]
          android:alwaysRetainTaskState=["true" | "false"]
          android:autoRemoveFromRecents=["true" | "false"]
          android:banner="drawable resource"
          android:clearTaskOnLaunch=["true" | "false"]
          android:colorMode=[ "hdr" | "wideColorGamut"]
          android:configChanges=["mcc", "mnc", "locale",
                                 "touchscreen", "keyboard", "keyboardHidden",
                                 "navigation", "screenLayout", "fontScale",
                                 "uiMode", "orientation", "density",
                                 "screenSize", "smallestScreenSize"]
          android:directBootAware=["true" | "false"]
          android:documentLaunchMode=["intoExisting" | "always" |
                                  "none" | "never"]
          android:enabled=["true" | "false"]
          android:excludeFromRecents=["true" | "false"]
          android:exported=["true" | "false"]
          android:finishOnTaskLaunch=["true" | "false"]
          android:hardwareAccelerated=["true" | "false"]
          android:icon="drawable resource"
          android:immersive=["true" | "false"]
          android:label="string resource"
          android:launchMode=["standard" | "singleTop" |
                              "singleTask" | "singleInstance"]
          android:lockTaskMode=["normal" | "never" |
                              "if_whitelisted" | "always"]
          android:maxRecents="integer"
          android:maxAspectRatio="float"
          android:multiprocess=["true" | "false"]
          android:name="string"
          android:noHistory=["true" | "false"]  
          android:parentActivityName="string" 
          android:persistableMode=["persistRootOnly" | 
                                   "persistAcrossReboots" | "persistNever"]
          android:permission="string"
          android:process="string"
          android:relinquishTaskIdentity=["true" | "false"]
          android:resizeableActivity=["true" | "false"]
          android:screenOrientation=["unspecified" | "behind" |
                                     "landscape" | "portrait" |
                                     "reverseLandscape" | "reversePortrait" |
                                     "sensorLandscape" | "sensorPortrait" |
                                     "userLandscape" | "userPortrait" |
                                     "sensor" | "fullSensor" | "nosensor" |
                                     "user" | "fullUser" | "locked"]
          android:showForAllUsers=["true" | "false"]
          android:stateNotNeeded=["true" | "false"]
          android:supportsPictureInPicture=["true" | "false"]
          android:taskAffinity="string"
          android:theme="resource or theme"
          android:uiOptions=["none" | "splitActionBarWhenNarrow"]
          android:windowSoftInputMode=["stateUnspecified",
                                       "stateUnchanged", "stateHidden",
                                       "stateAlwaysHidden", "stateVisible",
                                       "stateAlwaysVisible", "adjustUnspecified",
                                       "adjustResize", "adjustPan"] >
    . . .
</activity>
属性 説明
android\:allowEmbedded アクティビティを別のアクティビティの子として埋め込み、起動できることを示します。これは特に、別のアクティビティが所有するディスプレイなどのコンテナに子が存在する場合です。たとえば、Wear のカスタム通知に使用されるアクティビティは、Wear が別のプロセスに存在するコンテキスト ストリーム内でこのアクティビティを表示できるようにするため、この宣言が必要です。この属性のデフォルト値は false です。
android\:allowTaskReparenting タスクが次に前面に移動したとき、アクティビティを開始したタスクからアフィニティを持つタスクにアクティビティを移動できるかどうか。移動できる場合は "true"、アクティビティを開始したタスクに留まる必要がある場合は "false" を指定します。この属性が設定されていない場合、対応する 要素の allowTaskReparenting 属性によって設定された値がアクティビティに適用されます。デフォルト値は "false" です。通常、アクティビティを開始すると、アクティビティを開始したタスクにアクティビティが関連付けられ、アクティビティの生存期間中はそこに留まります。この属性を使用すると、現在のタスクが表示されなくなったときに、アフィニティを持つタスクをアクティビティの親として再割り当てすることができます。一般に、アプリに関連付けられたメインタスクにアプリのアクティビティを移動する場合に使用します。たとえば、メール メッセージにウェブページへのリンクが含まれている場合、リンクをクリックすると、そのウェブページを表示できるアクティビティが起動します。そのアクティビティはブラウザアプリで定義されていますが、メールタスクの一部として起動されます。ブラウザタスクをアクティビティの親として再割り当てすると、ブラウザが次に前面に移動したときにアクティビティが表示され、メールタスクが再び前面に移動したときにはそのアクティビティが表示されなくなります。アクティビティのアフィニティは、taskAffinity 属性で定義されます。タスクのアフィニティは、タスクのルート アクティビティのアフィニティを読み取って決定されます。したがって、定義上は、ルート アクティビティは常に、同じアフィニティを持つタスク内に存在します。起動モードに "singleTask" または "singleInstance" が設定されたアクティビティはタスクのルートにのみ存在できるため、親の再割り当ては "standard" モードと "singleTop" モードに限定されます。(launchMode 属性もご覧ください。)
android\:alwaysRetainTaskState アクティビティが割り当てられているタスクの状態を常にシステムで維持するかどうか。維持する場合は "true"、特定の状況でシステムがタスクを初期状態にリセットできるようにするには "false" を指定します。デフォルト値は "false" です。この属性が重要なのは、タスクのルート アクティビティのみです。その他すべてのアクティビティについては無視されます。通常は、ユーザーがホーム画面からタスクを選択し直したような状況で、システムはタスクをクリアします(ルート アクティビティ上のスタックからすべてのアクティビティを削除します)。一般に、ユーザーが特定の時間(たとえば 30 分間)、タスクにアクセスしなかった場合にこの処理が行われます。ただし、この属性が "true" の場合、ユーザーがどのような方法でタスクにアクセスしても、常に前回のタスクの状態に戻ります。これは、たとえば、ウェブブラウザのようなアプリで便利です。これにより、ユーザーは多くの状態(開いている複数のタブなど)をそのまま維持することができます。
android\:autoRemoveFromRecents この属性を指定したアクティビティによって開始されたタスクについて、タスク内の最後のアクティビティが完了するまでオーバービュー画面に表示し続けるかどうか。true の場合、タスクが自動的にオーバービュー画面から削除されます。これは、呼び出し元による FLAG_ACTIVITY_RETAIN_IN_RECENTS の使用よりも優先されます。"true" または "false" のブール値を指定する必要があります。
android\:banner 関連するアイテムに拡張されたグラフィック バナーを提供するドローアブル リソース。 タグで使用すると、特定のアクティビティにデフォルトのバナーを配置します。 タグで使用すると、アプリのすべてのアクティビティにバナーを配置します。Android TV のホーム画面では、システムがこのバナーを使用してアプリを表示します。バナーはホーム画面のみに表示されるため、CATEGORY_LEANBACK_LAUNCHER インテントを処理するアクティビティを持つアプリでのみ指定する必要があります。この属性は、画像を含むドローアブル リソースへの参照(たとえば "@drawable/banner")として設定する必要があります。デフォルトのバナーはありません。詳細については、TV アプリのビルドを開始するページのホーム スクリーンにバナーを配置するをご覧ください
android\:clearTaskOnLaunch タスクがホーム画面から再起動されたときに、ルート アクティビティを除くすべてのアクティビティをタスクから削除するかどうか。常にタスクのルート アクティビティ以外をクリアする場合は "true"、そうでない場合は "false" を指定します。デフォルト値は "false" です。この属性は、新しいタスクを開始するアクティビティ(ルート アクティビティ)にのみ影響します。タスクのその他すべてのアクティビティでは、この属性は無視されます。値が "true" の場合は、タスクの直前の操作や、タスクから離れるときに [戻る] ボタンまたは [ホーム] ボタンを押したかどうかにかかわらず、ユーザーが再びタスクを開始するたびにルート アクティビティから開始します。値が "false" の場合、状況によってはタスクのアクティビティがクリアされることがありますが、常にクリアされるわけではありません(alwaysRetainTaskState 属性をご覧ください)。たとえば、ホーム画面からアクティビティ P を起動し、そこからアクティビティ Q に移ったとします。ユーザーが [ホーム] を押して、再びアクティビティ P に戻りました。通常、ユーザーにはアクティビティ Q が表示されます。前回、P のタスクで Q を実行していたからです。ただし、P でこのフラグが "true" に設定されていると、P をベースにするすべてのアクティビティ(この場合は Q)は、ユーザーが [ホーム] を押したときに削除され、タスクはバックグラウンドに移動します。そのため、ユーザーがタスクに戻ると、P のみが表示されます。この属性と allowTaskReparenting の両方が "true" の場合、親の再割り当てが可能なアクティビティはすべて、アフィニティを共有するタスクに移動され、残りのアクティビティは上記のとおり削除されます。
android\:colorMode 対応端末でアクティビティを広色域モードで表示するようにリクエストします。広色域モードでは、SRGB よりも広い色域でウィンドウをレンダリングして、より鮮やかな色を表示できます。端末で広色域レンダリングがサポートされていない場合、この属性を指定しても効果はありません。ワイドカラー モードでのレンダリングの詳細については、ワイドカラー コンテンツによるグラフィックの拡張をご覧ください。
android\:configChanges アクティビティで処理する設定変更のリストを指定します。実行時に設定の変更が発生すると、デフォルトではアクティビティはシャットダウンおよび再起動されますが、この属性で設定を宣言しておくと、アクティビティの再起動を防止できます。代わりに、アクティビティは実行中のままになり、アクティビティの onConfigurationChanged() メソッドが呼び出されます。

ほかにもありますが、詳細は本家のページを参照ください。

このマニフェストで、各アクティビティの設定を行います。

次回は、画面(アクティビティ)から画面(アクティビティ)を呼び出す方法を学習します。>次回は、画面(アクティビティ)から画面(アクティビティ)を呼び出す方法を学習します。

アクティビティの作成

AndroidStudioでプロジェクトを作成すると、「MainActivity」クラスが出来上がっています。
このクラスが上記のアクティビティになります。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

このアクティビティ(画面)を作成しているのは、
setContentView(R.layout.activity_main);の部分になります。
R.layout.activity_mainの部分は、下の図のように、「activity_main.xml」に定義(描かれて)されています。

「activity_main.xml」を開くと右上のほうにのようなボタンがあるので、「Design」を選択すると画面のイメージが表示されます。

ここで、テキスト「Hello World」が表示されています。この部品が「TextView」という部品です。
この部品を移動してタイトルとして使用しようと思っていますが、その前に。。。

パッケージの修正

デフォルトで作成されたパッケージは「com.example.プロジェクト名」になっていると思います。
これを自作のパッケージ名に修正したいと思いますが、ただ単に修正するとエラーになります。

これは、AndroidManifest.xmlの修正も必要なためです。
使用するパッケージ名を上のXMLに記述します。今回作成するパッケージ名は「jp.zenryoku.mokuhyotasseiap」としてます。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.zenryoku.mokuhyotasseiap">

これで、エラーが解消され、Javaファイルも下のようにエラーが消えました。

多言語対応

作成するアプリは、多言語対応で作成したいと考えております。なので、こちらのページを参考に、多言語化を行います。

まとめると、下の図のように各言語に対応するXMLファイルを作成するというところです。

resを右クリックして、フォルダ名「values」の後ろに対応する言語を付けます。

  • 日本語(values-ja)
  • 英語(values-en)

そして、それぞれのstring.xmlに各言語に対応する文字列を記述します。

XMLの参照方法

多言語の文字列を設定することができたら、次は画面のコンポーネント(部品)にその値を設定します。
必要な処理ととしては、次のものになります。

  1. コンポーネントの取得
  2. 文字列の取得
  3. 値の設定
// コンポーネントの取得
TextView titleView = findViewById(R.id.appTitle);
// 値の設定
titleView.setText(R.string.app_name);

上のような実装で行けそうです。まだ起動確認していません。。。

バーチャルデバイスのインストール

実機がない場合は仮想デバイス(バーチャルデバイス)をインストールする必要があります。
動かしたときのイメージを見るためです。
これは、画面操作で簡単に行けます。

しかしエラーが!

Execution failed for task ':app:compressDebugAssets'.

上記のようなエラーが出ました。
こちらのサイトを参考にすると、Gradleのバージョンを下げてやればいけるというところで。。。

それぞれのバージョンを以下のように設定して再実行しました。
Android Gradle Plugin: 4.2.1
Gradle: 6.7.1

次のエラー

Caused by: org.gradle.internal.metaobject.AbstractDynamicObject$CustomMessageMissingMethodException: Could not find method dependencyResolutionManagement() for arguments

これもGradleのバージョンを次のように変えてやればよい感じでした。
Android Gradle Plugin: 7.0
Gradle: 7.0

しかし、使用しているPCの容量不足のため、AndroidStudioが落ちました。。。
Android開発は、十分にHDの要領があるPCを使用しないと駄目なようです。。。

でわでわ。。。

FreeTts エラー ~mbrola.base

FreeTtsエラー

下のような警告があり、修正してみました。

System property "mbrola.base" is undefined. Will not use MBROLA voices.

インストールしたFreeTtsのディレクトリに「mbrola」というフォルダがあったのでシステムプロパティにそのパスを渡して実行すると下のようにエラーが出ました。

System.setProperty("mbrola.base", "D:\\Apps\\freetts-1.2\\mbrola");

Make sure you FULLY specify the path to
the MBROLA directory using the mbrola.base
system property.

調べてみると、JDKのあるフォルダlibの下にmbrola.jarをコピーしてやればOKということでした。

しかし、MBROLAの音声ファイル(Voice)がダウンロードできなかったので、(502 Bad Gateway)打つ手なしと判断しました。。。

Java 漢字 ひらがな変換 ~ICU4Jを使う~

ICU4Jを使う

Java Speach APIを使用して、入力したテキストを話させるアプリケーションを作成しようとしています。
しかし、日本語に対応したライブラリが見つけられず、また見つけても動かすに至りませんでした。

とりあえず動かせたのが、下のような形で動くFreeTtsというライブラリを使用したものです。

これで、入力した文字を次のように変換するための処理を行うのにICU4Jを使用するつもりです。本家のドキュメントはこちら

ICU4Jの追加

現在使用しているプロジェクト(IntelliJ IDEA)にICU4Jを使用するための設定を行います。
今回(毎回)使用するのはMavenです。Mavenではpom.xmlに依存関係を追加することで、様々なライブラリを使用することができます。
例えばSpringrameworkであれば下のように記載してMavenプロジェクトを更新(再ロード)すればOKです。

SpringBootの依存関係追加

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.5.5</version>
</dependency>

注意点としては、参照するリポジトリへアクセスできるかどうか?という問題がありますが、プロジェクトを更新(再ロード)してできなければリポジトリを追加してやればOKです。※ICU4Jの場合です。

   <repositories>
        <repository>
            <id>icu4j</id>
            <url>https://repo1.maven.org/maven2/</url>
        </repository>
    </repositories>

ICU4Jの追加

ずばり、下のように書きます。チェックすることとしては、最新のバージョンが2.6.1でよいかどうかです。

    <dependencies>
        <dependency>
            <groupId>com.ibm.icu</groupId>
            <artifactId>icu4j</artifactId>
            <version>2.6.1</version>
        </dependency>
    </dependencies>

これで、プロジェクトを更新(再ロード)すれば、ライブラリが追加されています。

これで実装する準備が整いました。

ICU4Jの実装

以前書いた記事でコピーして作成したコードを改造して実行するつもりなので、先に以前作成したコードを改造します。

初めに着手する部分は、BriefVoiceDemoクラスのメインメソッド部分を、部品化することです。
つまりは、メインメソッドはプログラムを起動する部分なので、これを起動される側に変更するというわけです。

具体的には

下のように、プログラムが書いてあります。下のコード中にコメントで次のような部分は自分が追記した部分であり
説明のための文言です。

【改修ポイント】

Synthesizer synthesizer;

public static void main(String[] args) {

    //default synthesizer values
    SynthesizerModeDesc modeDesc = new SynthesizerModeDesc(
            null,       // engine name
            "general",  // mode name use 'general' or 'time'
            Locale.JAPANESE,  // locale, see MBROLA Project for i18n examples
            null,       // prefer a running synthesizer (Boolean)
            null);      // preload these voices (Voice[])

    //default voice values
    Voice voice = new Voice(
            "kevin16",              //name for this voice
            Voice.AGE_DONT_CARE,   //age for this voice
            Voice.GENDER_DONT_CARE,//gender for this voice
            null);                 //prefer a running voice (Boolean)

    boolean error=false;
    for (int r=0;r<args.length;r++) {
        String token= args[r];
        String value= token.substring(2);

        //overide some of the default synthesizer values
        if (token.startsWith("-E")) {
            //modeDesc.setEngineName(value);
        } else if (token.startsWith("-M")) {
            //modeDesc.setModeName(value);
        } else
            //overide some of the default voice values
            if (token.startsWith("-V")) {
                voice.setName(value);
            } else if (token.startsWith("-GF")) {
                voice.setGender(Voice.GENDER_FEMALE);
            } else if (token.startsWith("-GM")) {
                voice.setGender(Voice.GENDER_MALE);
            } else
            //dont recognize this value so flag it and break out
            {
                System.out.println(token+
                        " was not recognized as a supported parameter");
                error = true;
                break;
            }
    }

    /* 1.【改修ポイント】メインメソッドは、自分のクラスをnewして動かすので、部品かするにはこのnew以降の処理がいらない */
    //The example starts here
    BriefVoiceDemo briefExample = new BriefVoiceDemo();
    if (error) {
        System.out.println("BriefVoiceDemo -E<ENGINENAME> " +
                "-M<time|general> -V<VOICENAME> -GF -GM");
        //list all the available voices for the user
        briefExample.listAllVoices();
        System.exit(1);
    }

    //select synthesizer by the required parameters
    briefExample.createSynthesizer(modeDesc);
    //print the details of the selected synthesizer
    briefExample.printSelectedSynthesizerModeDesc();

    //allocate all the resources needed by the synthesizer
    briefExample.allocateSynthesizer();

    //change the synthesisers state from PAUSED to RESUME
    briefExample.resumeSynthesizer();

    //set the voice
    briefExample.selectVoice(voice);
    //print the details of the selected voice
    briefExample.printSelectedVoice();

    /* 2.【改修ポイント】SpeakableListenerは必要な部品なので、削除しない */
    //create a listener to be notified of speech events.
    SpeakableListener optionalListener= new BriefListener();

    /* 3.【改修ポイント】ここから先は、読み上げ処理を実行する部分なので
     *    メインメソッドから呼び出されるように修正する。つまりは、削除する。
     */
    //The Date and Time can be spoken by any of the selected voices
    SimpleDateFormat formatter = new SimpleDateFormat("h mm");
    String dateText = "The time is now " + formatter.format(new Date());
    briefExample.speakTextSynchronously(dateText, optionalListener);

    //General text like this can only be spoken by general voices
    if (briefExample.isModeGeneral()) {
        //speak plain text
        String plainText =
                "Hello World, This is an example of plain text," +
                        " any markup like <jsml></jsml> will be spoken as is";
        briefExample.speakTextSynchronously(plainText, optionalListener);

        //speak marked-up text from Speakable object
        Speakable speakableExample = new BriefSpeakable();
        briefExample.speakSpeakableSynchronously(speakableExample,
                optionalListener);
    }
    //must deallocate the synthesizer before leaving
    briefExample.deallocateSynthesizer();
}

上のコード内にある「【改修ポイント】」読んでもらえば、どの部分を削除するのか、そのまま残すのか?がわかると思います。
最終的に変更したコードは、下のようになります。

private Synthesizer synthesizer;

private SpeakableListener optionalListener;

public BriefVoiceCls() {

    //default synthesizer values
    SynthesizerModeDesc modeDesc = new SynthesizerModeDesc(
            null,       // engine name
            "general",  // mode name use 'general' or 'time'
            Locale.JAPANESE,  // locale, see MBROLA Project for i18n examples
            null,       // prefer a running synthesizer (Boolean)
            null);      // preload these voices (Voice[])

    //default voice values
    Voice voice = new Voice(
            "kevin16",              //name for this voice
            Voice.AGE_DONT_CARE,   //age for this voice
            Voice.GENDER_DONT_CARE,//gender for this voice
            null);                 //prefer a running voice (Boolean)
    // シンセサイザーのセットアップ
    this.createSynthesizer(modeDesc);
    //print the details of the selected synthesizer
    this.printSelectedSynthesizerModeDesc();

    //allocate all the resources needed by the synthesizer
    this.allocateSynthesizer();

    //change the synthesisers state from PAUSED to RESUME
    this.resumeSynthesizer();

    //set the voice
    this.selectVoice(voice);
    //print the details of the selected voice
    this.printSelectedVoice();
    // ここでセットアップ処理はおしまい。

    //create a listener to be notified of speech events.
    optionalListener = new BriefListener();
}

/** このメソッドで話をするようにプログラムを作る。 */
public void execute(String talkMessage) {

}

BriefVoiceCls#execute()を外部(メインメソッド)から呼び出し、プログラムを実行する形で必要な処理を行えるように実装します。

要件を考える

やりたいことは次の通りです。画面(JavaFX)から入力した文字を読み上げる

なので、シンプルに、実行するメソッドの引数には入力した文字列を渡します。
そして、コンストラクタでセットアップ処理、ここでは、シンセサイザーのセットアップや使用する声(Voice)を選択するなどの処理を行っています。
<シンセサイザーのセットアップ>

//default synthesizer values
SynthesizerModeDesc modeDesc = new SynthesizerModeDesc(
       null,       // engine name
       "general",  // mode name use 'general' or 'time'
       Locale.JAPANESE,  // locale, see MBROLA Project for i18n examples
      null,       // prefer a running synthesizer (Boolean)
      null);      // preload these voices (Voice[])

<音声のセットアップ>

//default voice values
Voice voice = new Voice(
        "kevin16",              //name for this voice
        Voice.AGE_DONT_CARE,   //age for this voice
        Voice.GENDER_DONT_CARE,//gender for this voice
        null);                 //prefer a running voice (Boolean)

そして、リスナーも使用しているので残してあります。※デバック用に使用しているようです。

/**
 * Simple SpeakableListener
 *   Prints event type and the source object's toString()
 */
public class BriefListener implements SpeakableListener {

    private String formatEvent(SpeakableEvent event) {
        return event.paramString()+": "+event.getSource();
    }

    public void markerReached(SpeakableEvent event) {
        System.out.println(formatEvent(event));
    }

    public void speakableCancelled(SpeakableEvent event) {
        System.out.println(formatEvent(event));
    }

    public void speakableEnded(SpeakableEvent event) {
        System.out.println(formatEvent(event));
    }

    public void speakablePaused(SpeakableEvent event) {
        System.out.println(formatEvent(event));
    }

    public void speakableResumed(SpeakableEvent event) {
        System.out.println(formatEvent(event));
    }

    public void speakableStarted(SpeakableEvent event) {
        System.out.println(formatEvent(event));
    }

    public void topOfQueue(SpeakableEvent event) {
        System.out.println(formatEvent(event));
    }

    public void wordStarted(SpeakableEvent event) {
        System.out.println(formatEvent(event));
    }
}

処理のほとんどが、標準出力に情報を表示するものです。これで、入力した文字を引数に受けて処理を行う準備が整いました。

ICU4Jを使用する

世間でよく使用される言葉として「オブジェクト指向プログラミング」というのがあります。この言葉は、ヒトによっていろいろな解釈があるのでなるべく使わないようにしようと思います。

今回の実装方法としては、上記のようなもののことなのですが「具体的にどうやるか?」を中心に記載したいと思います。

追記すコードは極力少なくする

この記事で、余計な処理=new BriefVoice()のようにインスタンス化してからの処理、を削除しました。
そして、新たに作成したメソッドは「execute()」メソッドです。引数は、Stringで入力した文字が渡される想定です。

つまるところ、BriefVoiceClsのexecute()メソッドを呼び出してやればOKという形にしました。
具体的には、次のようなコードになります。

/** このメソッドで話をするようにプログラムを作る。 */
public void execute(String talkMessage) {
    this.speakTextSynchronously(talkMessage, optionalListener);
}

ここで残る問題は、本記事のタイトルであるICU4Jを使用する、漢字を含む日本語をすべてひらがなに変換する処理を作成することです。

ICU4Jで変換処理を実装

ここで、漢字→ひらがなへの変換処理を担当するクラスを作成します。
クラスの名前は「KanjiConverter」にします。そして、テストファーストの形で実装します。

初めに作成したのは、実際に動かすクラス(KanjiConverter)とこのクラスをテストするためのクラス(KanjiConverterTest)です。
そして、ICU4Jを使用する方法がわからないのでそれを調べます。

参考サイトはこちらです。

ICU4Jは漢字に対応してない?

調べてみると[「kuromoji」]()というライブラリも使用する必要があるということでした。
なので、この依存関係を追加します。※プロジェクトの再ロードを忘れないようにしましょう。

【kuromojiの使い方】
こちらのサイトを参考にしました。
まずは、kuromojiを使用してみるということで、下のようなコードをテストクラスで実行しました。

public class KanjiConverterTest {
    /** static メソッドはstatic修飾子がついてないと参照できない */
    private static KanjiConverter target;

    /** テストクラスをインスタンス化する時に行う処理 */
    @BeforeClass
    public static void init() {
        // 前処理でテスト対象クラスをインスタンス化
        target = new KanjiConverter();
    }

    /** インスタンスが解放されるとき、ガベージコレクションで実行 */
    @AfterClass
    public static void terminated() {
        target = null;
    }

    /** ICU4Jをとりあえず起動してみる */
    @Test
    public void test1() {
        Tokenizer tokeni = new Tokenizer();
        List<Token> list = tokeni.tokenize("本日は晴天なり");
        for (Token t: list) {
            System.out.println("Length: " + t.getAllFeatures().split(",").length);
            System.out.println(t.getSurface() + "\t" + t.getAllFeatures());
        }
    }
}

これで、出y録した結果が以下になります。

Length: 9
本日 名詞,副詞可能,,,,,本日,ホンジツ,ホンジツ
Length: 9
は 助詞,係助詞,,,,,は,ハ,ワ
Length: 9
晴天 名詞,一般,,,,,晴天,セイテン,セイテン
Length: 9
なり 助動詞,,,*,文語・ナリ,基本形,なり,ナリ,ナリ

取得した配列(カンマで区切られた数)は9個で固定のようです。

ならば、入力した文字列を各単語に分けてやればよさそうです。つまり、次の手順を踏みます。

  1. 各カタカナに対応する、発音文字列を前もってマップしておく
  2. 各単語をカタカナに変換
  3. 対応する発音をFreeTtsでスピーチさせる

実装する(テストクラス)

まずは、音声に変換するための文字列をマッピングします。なので、初めのBriefVoiceDemoクラスで書く発音の文字列を決めます。
次のような感じで発音できました。

これらの文字列と各カタカナを関連付けていきます。要領としては以下の通りです。

private static final String[] KANA_LIST = {"ア", "イ", "ウ", "エ", "オ", // 1
                                            "カ", "キ", "ク", "ケ", "コ", // 2
                                            "ガ", "ギ", "グ", "ゲ", "ゴ", // 3
                                            "サ", "シ", "ス", "セ", "ソ", // 4
                                            "ザ", "ジ", "ズ", "ゼ", "ゾ", // 5
                                            "タ", "チ", "ツ", "テ", "ト", // 6
                                            "ダ", "ヂ", "ヅ", "デ", "ド", // 7
                                            "ナ", "ニ", "ヌ", "ネ", "ノ", // 8
                                            "ハ", "ヒ", "フ", "ヘ", "ホ", // 9
                                            "バ", "ビ", "ブ", "ベ", "ボ", // 10
                                            "マ", "ミ", "ム", "メ", "モ", // 11
                                            "ヤ", "ユ", "ヨ", // 12
                                            "ラ", "リ", "ル", "レ", "ロ", // 13
                                            "ワ", "ヲ", "ン", // 14
                                        };

String[] moji = {"ah", "yee" , "hu", "a", "oh" // 1
    , "kah", "kee", "ku", "ckea", "koh" // 2
    , "gaah", "gy", "goo", "gue", "goh" // 3
    , "saeh", "see", "su", "thea", "soh" // 4
    , "zaeh", "zee", "zoo", "zea", "zoh" // 5
    , "taeh", "tiee", "tsu", "te", "toh" // 6
    , "daeh", "dgee", "do", "de", "doh" // 7
    , "naeh", "niee", "nuh", "nea", "noh" // 8
    , "haeh", "hiee", "hu", "hea", "hoh" // 9
    , "baeh", "bee", "boo", "be", "boh" // 10
    , "maeh", "miee", "muh", "me", "moh" // 11
    , "yaeh", "yu", "yoh" // 12
    , "ra", "ri", "ru", "re", "roh" // 13
    , "wa", "oh", "um"}; // 14

// サイズ(長さ)はおなじなので
for (int i = 0; i < KANA_LIST.length; i++) {
    String key = KANA_LIST[i];
    String value = moji[i];
    talkMap.put(key, value);
}

これで入力文字を発音用の文字列に変換し再生します。作成したクラスは次の通りです。

public class BriefVoiceClsTest {
    private static BriefVoiceCls target;
    @BeforeClass
    public static void init() {
        target = new BriefVoiceCls();
    }

    @Test
    public void testTalkVoice() {
        target.execute("本日は晴天なり");
    }
}

<実行結果>

文字を発音用の文字に変換する

今までに、カタカナは変換できましたが、英語や数字に関しては、触れていませんでした。
なので、入力した部分(文字)はNULLになり、スペースで何も発音されません。
ここで、プログラムに修正を加えます。

カタカナを変換するクラス「KanjiConverter」を修正してやります。
マップrに登録した値を変換→カタカナ1文字を発音用の文字列に変換します。

for (char ch : chars) {
    String note = this.talkMap.get(String.valueOf(ch));
    String append = note == null ? " " : note;
    build.append(append + " ");
}

これで変数chに「ア」が入っているときは「ah」という文字列に変換されます。
具体的には「String note = this.talkMap.get(String.valueOf(ch));」の部分で変数「note」に値が入ります。

この状態で「本日は晴天なり」と入力したときには「hoh um zee tsu wa thea yee te um naeh ri 」という文字列に変換されます。
この文字列を発音(Speach)させるとそれっぽく聞こえます。

これで、発音用の文字列に変換できるのですが、以下の部分で想定通りに動きません。

  1. 普通に英語を話したいとき
  2. 数字を読むとき

これらを解決するには、文章を単語に分解した後に、最後の部分、以下のコードを参照ください。

public String convert(String inputText) {
    List<Token> list = tokenizer.tokenize(inputText);
    StringBuilder build = new StringBuilder();

    for (Token token : list) {
        String[] splits = token.getAllFeatures().split(",");
        if (splits[8].equals("*")) {
            build.append(token.getSurface() + " ");
        } else {
            build.append(splits[8] + " ");
        }
    }
    return build.toString();
}

入力した文字列を変数(引数)「inputText」に渡した状態で処理を行います。例えば「本日は晴天なり」がinputTextに入ります。
これを単語に分解しているのが、token.getAllFeatures().split(",");の部分であり、返り値(String[])の8番目には
カタカナが入るのですが、英語や数字の場合は「*」が入っていますのでその場合は単語をそのまま取得して返却する文字列に追加します。

具体的には、下のコードです。

if (splits[8].equals("*")) {
    build.append(token.getSurface() + " ");
} else {
    build.append(splits[8] + " ");
}

でわでわ。。。

IntelliJ IDEA Maven リポジトリからロードできない

Mavenでソースをロードできない

IntelliJ IDEAを使用してpom.xmlにMavenリポジトリからソースをロードしようとすると下のような文言が出てロードできない事象にあいました。

依存関係 'com.ibm.icu:icu4j:2.9.1' が見つかりません

これは、間違っているので、エラーになっているのですが。正しくは、下のような形でpom.xmlを書きます。

<dependencies>
    <dependency>
        <groupId>com.ibm.icu</groupId>
        <artifactId>icu4j</artifactId>
        <version>2.6.1</version>
    </dependency>
</dependencies>

これでロードできるはずなのですが、出来ない。。。
こちらのページを参考にすると、「Mavenの更新ができていないから」ということでした。
下のような操作を行います。

  1. プロジェクトを右クリック
  2. Mavenを選択
  3. プロジェクトの再ロード

これで、ソース(JARなど)をロードすることができます。

でわでわ。。。

Java Speach APIを学ぶ(遊ぶ)

Java Speach API(JSAPI)

セットアップ

こちらのサイトを参考にインストールしました。
結局はライブラリをインストールする形になりました。

  1. ライブラリをSourceForgeからダウンロードします。

  2. これを展開して中にあるjsapi.exeを実行する

    この例では、D:\ apps \フォルダーが使用されます
    D:\apps\freetts-1.2.1\lib jsapi.exeに移動し て実行します。

  3. これによりjsapi.exeが作成されるようですが、初めからありました。

  4. jsapi.exeのあるディレクトリ(フォルダ)をライブラリとして指定します。

    1. 上部メニューのファイルを選択
    2. プロジェクトの構造を選択
    3. ライブラリの作成
    4. プロジェクトに追加されていることを確認
    5. プロパティファイルをJDK/jre/libに配置します。※ D:\Apps\jdk1.8.0_265\jre\lib

      D:\apps\freetts-1.2.1\speech.properties ファイルを %user.home% または %java.home%/lib フォルダにコピーし ます。このファイルは、JSAPIが使用する音声エンジンを決定するために使用されます。
      具体的には、

実行

参考サイトに載っているコード(java)を三つコピーして作成しました。

  • BriefVoiceDemo.java
  • BriefSpeakable.java
  • BriefListener.java

日本語をしゃべらせる

調べてみると「mbrola」が日本語に対応する声を持っているようです。具体的にはfreettsのフォルダ内にある「mdrola」のことです。
とりあえずは、実行するためのコードを見てみるとMBROLAを使用しているようなので、ロケールをJAPANESEに変更し、動かしてみると...

「Locale.US」を「Locale.JAPANESE」に変更してあります。

//default synthesizer values
SynthesizerModeDesc modeDesc = new SynthesizerModeDesc(
        null,       // engine name
        "general",  // mode name use 'general' or 'time'
        Locale.JAPANESE,  // locale, see MBROLA Project for i18n examples
        null,       // prefer a running synthesizer (Boolean)
        null);      // preload these voices (Voice[])

下のようなエラーが出ました。

System property "mbrola.base" is undefined. Will not use MBROLA voices.
Unable to create synthesizer with the required properties

Be sure to check that the "speech.properties" file is in one of these locations:

mbrolaの設定が良くないようです。なのでMBROLAを調べることにします。

調べていくと次のページを見つけました。ここに細かいところの記載があるので、これを参考にしてみます。

しかし、必要なファイルなどがダウンロードできません。。。。
結局はGithubにありました。

これをダウンロード(ZIP)して見ましたが、これもリンク切れが多く、調査が進みませんでした。。。。

しかし、英語を日本語調で発音させることはできるようです。

やはり、人工知能処理を入れないとできないようです。。。

ここであきらめない!

しかし、既製品のものがあります。FreeTtsも日本語での発音は実現しています。
詳細に関しては、商品化しているであろうため公開されていないと思われます。

やはり、下のような手順で行うのが無難なのかもしれません。

  1. 入力した文字をすべてひらがなに変換
  2. ひらがなをそれっぽい発音をする単語に関連図ける(Mapを使用する)
  3. 各単語をすべてアルファベットに変換
  4. 再生する

こんな方法しか見つかりませんでした。もちろん、TTSサービスを使用できるサイトなどたくさんあります。
しかし、Javaで自力で音声を再生したかったのです。。。

粘ってみた

とりあえずのところ、FreeTtsでは日本語をスピーチさせることが実現できない状態ですが、他にMaryTtsというライブラリ?がありました。
ソースをコンパイルして、起動すればサーバーとして起動できるようです。ここら辺に解決の糸口を見つけたいと思います。

MaryTts

MaryTtsをpom.xmlに追加して、MavenでJARを追加しました。
そして、ここにMaryTtsの新言語の追加方法が書いてありました。
GithubのWikiページですね。
ここを読み進めてみます。

しかし、色々と躓き断念することにしました。。。

FreeTtsで頑張る

結局のところ上に記載した方法で実装することにしました。

  1. 入力した文字をすべてひらがなに変換
  2. ひらがなをそれっぽい発音をする単語に関連図ける(Mapを使用する)
  3. 各単語をすべてアルファベットに変換
  4. 再生する

そして、必要になる(あったら無難な)ライブラリを使用することにします。

  • ICU4J:漢字ひらがな変換ライブラリ:

漢字ひらがな変換ライブラリ

pom.xmlの設定では、リポジトリの指定と、依存関係の指定で追加できました。

<repositories>
    <repository>
        <id>icu4j</id>
        <url>https://repo1.maven.org/maven2/</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>com.ibm.icu</groupId>
        <artifactId>icu4j</artifactId>
        <version>2.6.1</version>
    </dependency>
</dependencies>

ICU4Jに関しては、こちらの記事を参照ください。

なんだかんだと実装した結果「本日は晴天なり」としゃべることに成功しました。

実装する(テストクラス)

まずは、音声に変換するための文字列をマッピングします。なので、初めのBriefVoiceDemoクラスで書く発音の文字列を決めます。
次のような感じで発音できました。

これらの文字列と各カタカナを関連付けていきます。要領としては以下の通りです。

private static final String[] KANA_LIST = {"ア", "イ", "ウ", "エ", "オ", // 1
                                            "カ", "キ", "ク", "ケ", "コ", // 2
                                            "ガ", "ギ", "グ", "ゲ", "ゴ", // 3
                                            "サ", "シ", "ス", "セ", "ソ", // 4
                                            "ザ", "ジ", "ズ", "ゼ", "ゾ", // 5
                                            "タ", "チ", "ツ", "テ", "ト", // 6
                                            "ダ", "ヂ", "ヅ", "デ", "ド", // 7
                                            "ナ", "ニ", "ヌ", "ネ", "ノ", // 8
                                            "ハ", "ヒ", "フ", "ヘ", "ホ", // 9
                                            "バ", "ビ", "ブ", "ベ", "ボ", // 10
                                            "マ", "ミ", "ム", "メ", "モ", // 11
                                            "ヤ", "ユ", "ヨ", // 12
                                            "ラ", "リ", "ル", "レ", "ロ", // 13
                                            "ワ", "ヲ", "ン", // 14
                                        };

String[] moji = {"ah", "yee" , "hu", "a", "oh" // 1
    , "kah", "kee", "ku", "ckea", "koh" // 2
    , "gaah", "gy", "goo", "gue", "goh" // 3
    , "saeh", "see", "su", "thea", "soh" // 4
    , "zaeh", "zee", "zoo", "zea", "zoh" // 5
    , "taeh", "tiee", "tsu", "te", "toh" // 6
    , "daeh", "dgee", "do", "de", "doh" // 7
    , "naeh", "niee", "nuh", "nea", "noh" // 8
    , "haeh", "hiee", "hu", "hea", "hoh" // 9
    , "baeh", "bee", "boo", "be", "boh" // 10
    , "maeh", "miee", "muh", "me", "moh" // 11
    , "yaeh", "yu", "yoh" // 12
    , "ra", "ri", "ru", "re", "roh" // 13
    , "wa", "oh", "um"}; // 14

// サイズ(長さ)はおなじなので
for (int i = 0; i < KANA_LIST.length; i++) {
    String key = KANA_LIST[i];
    String value = moji[i];
    talkMap.put(key, value);
}

これで入力文字を発音用の文字列に変換し再生します。作成したクラスは次の通りです。

public class BriefVoiceClsTest {
    private static BriefVoiceCls target;
    @BeforeClass
    public static void init() {
        target = new BriefVoiceCls();
    }

    @Test
    public void testTalkVoice() {
        target.execute("本日は晴天なり");
    }
}

<実行結果>

でわでわ。。。

でわでわ。。。

<IntelliJ IDEAを操作して時の動画リスト>

JavaCV エラー (-215:Assertion failed) !image.empty() in function ‘cv::imencode’ ]

OpenCVでエラー発生

下のようなエラーメッセージです。

Caused by: CvException [org.opencv.core.CvException: cv::Exception: OpenCV(4.4.0) C:\build\master_winpack-bindings-win64-vc14-static\opencv\modules\imgcodecs\src\loadsave.cpp:919: error: (-215:Assertion failed) !image.empty() in function 'cv::imencode'
]
at org.opencv.imgcodecs.Imgcodecs.imencode_1(Native Method)
at org.opencv.imgcodecs.Imgcodecs.imencode(Imgcodecs.java:378)
at zenryokuservice.opencv.fx.learn.LearnOpenCv.createBufferedImage(LearnOpenCv.java:137)
at zenryokuservice.opencv.fx.learn.LearnOpenCv.execute(LearnOpenCv.java:72)
at zenryokuservice.opencv.fx.controller.TestingCvController.clickExecute(TestingCvController.java:89)
... 59 more

実行したときのコード

        // 表示するイメージを取得
        URL url = getClass().getResource("/charactors/myFace.png");
        URL url_kanaB = getClass().getResource("/charactors/kanabo.png");
System.out.println(url.getPath());
System.out.println(url_kanaB.getPath());
        // 表示イメージを読み取る
        Mat charactor = Imgcodecs.imread(url.getPath());
        Mat gray = Imgcodecs.imread(url.getPath(), Imgcodecs.IMREAD_GRAYSCALE);
        Mat kanaImg = Imgcodecs.imread(url_kanaB.getPath());
        System.out.println(kanaImg);
        optImg = createBufferedImage(kanaImg, ".png");

<createBufferedImage()>

    private BufferedImage createBufferedImage(Mat img, String ext) throws IOException {
        MatOfByte matOfByte = new MatOfByte();
        Imgcodecs.imencode(ext, img, matOfByte); -> ここでエラー
         return ImageIO.read(new ByteArrayInputStream(matOfByte.toArray()));
    }

エラーになるのは「Imgcodecs.imencode(ext, img, matOfByte);」の部分です。

原因として考えられること

  1. imgを渡すのに、データが不正(画像が読み込めていないなど)
  2. ファイル拡張子が違う
  3. Matの出力結果から怪しいところをみる

    Mat [ -1-1CV_8UC1, isCont=false, isSubmat=false, nativeObj=0x1c407d00, dataAddr=0x0 ]

このうちの「dataAddr=0x0」というのがnull参照になっている?と疑問に思いました。

解決

結局のところは、BufferedImageを作るのに、Matクラスを使う必要がないので、したのようにImageIOを使用することにしました。

BufferedImage buf = ImageIO.read(url);

これで一応の解決をしました。

Blender Python ~オブジェクトの作成~

イントロダクション

前回は、BlenderでのPythonスクリプト画面の表示、サンプルコードの実行などを行いました。
Blenderは下のような画面で3Dモデルを作れるフリーアプリケーションです。

今回は、前回にあるメモと参考サイトを見ながら、オブジェクト(立方体や、円)の作成を行いたいと思います。

オブジェクト作成

まずは、Blenderで作品を作成するには、立方体などのオブジェクトの作成を行うと思います。
この手で行う操作を、プログラムで実行しようというところです。

使用するコマンドについて

ここでいう「コマンド」というのは、「bpy.ops.XXX」のようなスクリプトの事です。

このコマンドを実行することで、オブジェクトが作成されます。コマンドの一覧はこちら本家のサイトを見ました。

コーン(Cone)を作成する

引数に関しては、すべて使用していません。使用する引数の名前を付けて実行します。

bpy.ops.mesh.primitive_cone_add(vertices=32, end_fill_type='TRIFAN', calc_uvs=True, location=(0,0,1), rotation=(0.0, 0.0, 0.0))

具体的には「vertices=32」のように頂点=32という形で引数を渡します。

コマンドを実行すると下のように表示されます。

ここで、もう一つrotationの値を変更すると下のようになります。

実行コマンドは下のようになります。

bpy.ops.mesh.primitive_cone_add(vertices=32, end_fill_type='TRIFAN', calc_uvs=True, location=(0,0,1), rotation=(0.0, 1.0, 0.0))

X軸方向に1だけ回転しています。

同様に、locationの値を変更すれば下のようになります。

実行コマンドは下のようになります。

bpy.ops.mesh.primitive_cone_add(vertices=32, end_fill_type='TRIFAN', calc_uvs=True, location=(0,0,2), rotation=(0.0, 0.0, 0.0))
{'FINISHED'}

Z軸方向に2だけ移動しています。※もともと1移動していました。。。

連続してコーンを作成する

これに、プログラミングヨロシク、For文で縦に連続して作成してみます。
実行コードは下のようになります。

import bpy

for count in range(5):
     bpy.ops.mesh.primitive_cone_add(
        vertices=32
        , end_fill_type='TRIFAN'
        , calc_uvs=True
        , location=(0,0,count)
        , rotation=(0.0, 0.0, 0.0)
        , scale=(0.5, 0.5, 0.5))

scaleを半分にしました。つまりは「0.5」です。

次は、For文で横に連続して作成してみます。
実行コードは下のようになります。

import bpy

for count in range(5):
     bpy.ops.mesh.primitive_cone_add(
        vertices=32
        , end_fill_type='TRIFAN'
        , calc_uvs=True
, rotation=(0.0, 0.0, 0.0)
        , scale=(0.5, 0.5, 0.5))

ひと工夫してみる

  1. 横だけでなく縦にも並べる

    • ※X軸方向にも並べる処理を追加する
  2. 横人並べるた上で、それを一回転してみようというわけです。

なので、下のような出力が得られました。

実行コマンドは下のようになります。

import bpy

# change direction
for lines in range(0, 5):
    # put on line
    for count in range(0, 5):
         bpy.ops.mesh.primitive_cone_add(
            vertices=32
            , end_fill_type='NGON'
            , calc_uvs=True
            , location=(lines,count,0)
            , rotation=(0.0, 0.0, 0.0)
            , scale=(0.5, 0.5, 0.5))

ひと工夫してみる、その二

横だけでなく縦(Z軸方向)にも追加してみる

やり方は、上記のものと変わりません。ただし、X副方向ではなくZ軸方向に追加します。
実行結果

実行コマンドは下のようになります。

import bpy

# change height direction
for height in range(0,5):
    # change direction
    for lines in range(0, 5):
        # put on line
        for count in range(0, 5):
             bpy.ops.mesh.primitive_cone_add(
                vertices=32
                , end_fill_type='NGON'
                , calc_uvs=True
                , location=(lines,count,height)
                , rotation=(0.0, 0.0, 0.0)
                , scale=(0.5, 0.5, 0.5))

稀にみる3次元ループです(笑)

円状に並べてしてみる

実行結果

三角関数を使用してみました。数学嫌いだった過去が悔やまれる。。。

import bpy
import math

#for lines in range(0, 3):
for count in range(0, 11):
    rad = count * 36
    bpy.ops.mesh.primitive_cone_add(
        vertices=32
        , end_fill_type='NGON'
        , calc_uvs=True
        , location=(math.sin(rad), math.cos(rad), 0)
        , rotation=(0.0, 0.0, 0.0)
        , scale=(0.2, 0.2, 0.2))

まとめ

今回は、「コーン」で作成しましたが、これを「立方体」「モンキー(スザンヌ?)」「トーラス」と変えるだけで大して遊べると思います。

ペジェ曲線を作る(円)

、まずはサンプルコードから見ていきます。

from bpy import context, data, ops

# [1] Create a bezier circle and enter edit mode.
ops.curve.primitive_bezier_circle_add(radius=1.0,
                                      location=(0.0, 0.0, 0.0),
                                      enter_editmode=True)

# [2] Subdivide the curve by a number of cuts, giving the
# random vertex function more points to work with.
ops.curve.subdivide(number_cuts=16)

# [3] Randomize the vertices of the bezier circle.
# offset [-inf .. inf], uniform [0.0 .. 1.0],
# normal [0.0 .. 1.0], RNG seed [0 .. 10000].
ops.transform.vertex_random(offset=1.0, uniform=0.1, normal=0.0, seed=0)

# [4] Scale the curve while in edit mode.
ops.transform.resize(value=(2.0, 2.0, 3.0))

# Return to object mode.
ops.object.mode_set(mode='OBJECT')

[1]

ペジェ曲線の円を描画するbpy.ops.curve.primitive_bezier_circle_add

# Create a bezier circle and enter edit mode.
ops.curve.primitive_bezier_circle_add(radius=1.0,
                                      location=(0.0, 0.0, 0.0),
                                      enter_editmode=True)

まずはこれを作らないとはじまらない、そして、円もくねくねと曲げられるようです。
bpy.ops.curve.subdivide(number_cuts=1)

そして、これから、くねくねと曲げる処理ですが、下の部分がそれにあたります。

# Subdivide the curve by a number of cuts, giving the
# random vertex function more points to work with.
ops.curve.subdivide(number_cuts=16)

# Randomize the vertices of the bezier circle.
# offset [-inf .. inf], uniform [0.0 .. 1.0],
# normal [0.0 .. 1.0], RNG seed [0 .. 10000].
ops.transform.vertex_random(offset=1.0, uniform=0.1, normal=0.0, seed=0)

次はops.curve.subdivide(number_cuts=16)を見ていきます。

しかし、このままでは、よくわからないので、次の部分を16から8に変更します。
ちなみに、ops.transform.vertex_random(offset=1.0, uniform=0.1, normal=0.0, seed=0)の部分は
「頂点をランダム化する」処理のようです。

まとめると次のようになります。
1.4つの頂点を持つ円(ペジェ曲線)を作成: bpy.ops.curve.primitive_bezier_circle_add

  1. この4つの頂点を16回切る(細分化): bpy.ops.curve.subdivide(number_cuts=16)
    • 1回切ると頂点は4 -> 8, 2回切ると4 -> 12, 3回切ると4 -> 16
  2. これらの頂点えおランダムに移動させる: bpyops.transform.vertex_random(offset=1.0, uniform=0.1, normal=0.0, seed=0)

なので、3回切った時のオブジェクトは下のようになりました。

ペジェ曲線を作る(線)

上のコードを書き換えて実行してみます。[1]の部分を書き換えています。
ズバリprimitive_bezier_circle_add
*[primitive_bezier_curve_add](https://docs.blender.org/api/current/bpy.ops.curve.html?highlight=primitive#bpy.ops.curve.primitive_bezier_curve_add)*に書き換えるだけです

<実行したコード>

from bpy import context, data, ops

# [1] Create a bezier circle and enter edit mode.
ops.curve.primitive_bezier_curve_add(radius=1.0,
                                      location=(0.0, 0.0, 0.0),
                                      enter_editmode=True)

# [2] Subdivide the curve by a number of cuts, giving the
# random vertex function more points to work with.
ops.curve.subdivide(number_cuts=16)

# [3] Randomize the vertices of the bezier circle.
# offset [-inf .. inf], uniform [0.0 .. 1.0],
# normal [0.0 .. 1.0], RNG seed [0 .. 10000].
ops.transform.vertex_random(offset=1.0, uniform=0.1, normal=0.0, seed=0)

# [4] Scale the curve while in edit mode.
ops.transform.resize(value=(2.0, 2.0, 3.0))

# Return to object mode.
ops.object.mode_set(mode='OBJECT')

そうすると下のようなオブジェクトができました。

「なんだ、あまり変わらないではないか。。。」と感じた方、正常でございます。
自分もそう思います。

では、生成する頂点の数を減らしてみましょう。カットする回数を1回にします。

ops.curve.subdivide(number_cuts=1)

最終的な頂点の数は3つになりました。

まだまだ続きます。

でわでわ。。。

でわでわ。。。

Blender Python はじめて〜Pythonコマンド一覧

Blender Python tutorial

参考サイトはこちらです。

ショートカット一覧はこちら

Index(目次)

  1. よく使用される単語: 単語の表があります。
  2. クイックスタート: BlenderでのPythonスクリプト実行の準備とBMeshについて
  3. 円を作成する: Pythonコードと細かいPythonコマンドの説明
  4. Pythonでの描画サンプルコード
  5. ベストプラクティスで学習する
  6. Blender Python APモジュール

よく使用される単語

単語 内容
メッシュ Blenderでは、Vertex(点)、Edge(辺)、Face(面)で構成された3DオブジェクトのことをMesh(メッシュ)と呼びます。 .
ジオメトリ シェーディングポイントに関する幾何学的情報
ディゾルブ ディゾルブは、ジオメトリを削除し、周辺のジオメトリで埋めます。ジオメトリを削除するとできてしまう穴を、もう一度埋める作業の代わりに使用できます。

Pythonコマンド一覧

コマンド、内容、パラメータの順に記載していますが、パラメータは実際に入力する例として値が入っています。もちろんそのまま使用できます。

プリミティブの作成コマンド

No コマンド 内容 パラメータ(引数)
1 bpy.ops.mesh.primitive_circle_add 円メッシュを作成する 頂点= 32、半径= 1.0、fill_type = 'NOTHING' 、calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、スケール= (0.0、 0.0、 0.0) )
2 bpy.ops.mesh.primitive_cone_add 円錐メッシュを作成します 頂点= 32、radius1を= 1.0、radius2 = 0.0、深さ= 2.0、end_fill_type = 'Ngonの'、calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= ( 0.0、 0.0、 0.0) 、規模= (0.0、 0.0、 0.0)
3 bpy.ops.mesh.primitive_cube_add 立方体メッシュを作成します サイズ= 2.0、calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、規模= (0.0、 0.0、 0.0)
4 bpy.ops.mesh.primitive_cube_add_gizmo 立方体メッシュを作成します calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、規模= (0.0、 0.0、 0.0) 、マトリックス= ((0.0 、 0.0、 0.0、 0.0)、 (0.0、 0.0、 0.0、 0.0)、 (0.0、 0.0、 0.0、 0.0)、 (0.0、 0.0、 0.0、 0.0)
5 bpy.ops.mesh.primitive_cylinder_add 円柱メッシュを作成します 頂点= 32、半径= 1.0、深さ= 2.0、end_fill_type = 'Ngonの'、calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、規模= (0.0、 0.0、 0.0)
6 bpy.ops.mesh.primitive_grid_add グリッドメッシュを作成する x_subdivisions = 10、y_subdivisions = 10、サイズ= 2.0、calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、規模= ( 0.0、 0.0、 0.0)
7 bpy.ops.mesh.primitive_ico_sphere_add Icosphereメッシュを作成します 細分= 2、半径= 1.0、calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、規模= (0.0、 0.0、 0.0)
8 bpy.ops.mesh.primitive_monkey_add スザンヌメッシュを構築する サイズ= 2.0、calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、規模= (0.0、 0.0、 0.0)
9 bpy.ops.mesh.primitive_plane_add 4つの頂点を持つ塗りつぶされた平面メッシュを作成します サイズ= 2.0、calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、規模= (0.0、 0.0、 0.0)
10 bpy.ops.mesh.primitive_torus_add トーラスメッシュを作成します ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、major_segments = 48、minor_segments = 12、モード= 'MAJOR_MINOR' 、major_radius = 1.0、minor_radius = 0.25、abso_major_rad = 1.25、abso_minor_rad = 0.75、generate_uvs = True
11 bpy.ops.mesh.primitive_uv_sphere_add UV球メッシュを作成します セグメント= 32、ring_count = 16、半径= 1.0、calc_uvs =真、enter_editmode =偽、ALIGN = 'WORLD' 、位置= (0.0、 0.0、 0.0) 、回転= (0.0、 0.0、 0.0) 、規模= ( 0.0、 0.0、 0.0)

クイックスタート

下のような記載がありました。(日本語に翻訳)

【Blender Python APIの機能】
・ ユーザーインターフェイスで可能なデータ(シーン、メッシュ、パーティクルなど)を編集します。
・ユーザー設定、キーマップ、およびテーマを変更します。
・独自の設定でツールを実行します。
・メニュー、ヘッダー、パネルなどのユーザーインターフェイス要素を作成します。
・新しいツールを作成します。
・インタラクティブなツールを作成します。
・Blenderと統合する新しいレンダリングエンジンを作成します。
・データとそのプロパティへの変更をサブスクライブします。
・既存のBlenderデータに新しい設定を定義します。
・Pythonを使用して3Dビューポートで描画します。

始める前にやっておくと良いこと

Blender2.69使用

【設定画面を開く】
FIle -> User Preference
DeveloperExtra とPythonのツールチップを有効にします。

  • DeveloperExtra -> Blender2.69(mac版)には見つかりませんでした。。。
  • PythonTooltipsにチェックを入れる(デフォルトでチェックが入ってました)

Blender2.93使用

【スプラッシュ画面を開く】
※自動で表示されます。

ここで、言語の設定ができます。「言語」Japaneseに変更します。
そのほか、マウスのコントロールなど変更可能です。

【設定画面を開く】

インターフェース -> 表示 -> Pythonツールチップを表示

Pythonコンソールを使用する


上のキャプチャのように「default」から「Scripting」に変更します。

システムコンソールを表示する


しかしこのままだと、システムコンソールには日本語が文字ばけします。

なので、以下のようにBlenderを起動します。

Blender2.93使用


こちらも同様です。※クリックする場所が違うけど。。。

これで、pythonコードをテストするのにとても便利です。
そして、参考サイトの下の方に行くと、サンプルコードがあるので実行してみると動きがわかります。

それで物足りなければ、参考サイトの下のような部分を参照してみると良いです。

システムコンソールを開く

サンプルコード

  • Blender Python API
  • 操作オペレーションとコード(bpy.ops)
    1. オブジェクトの削除: bpy.ops.object.delete()
    2. Cubeの作成: bpy.ops.mesh.primitive_cube_add(location=(0,0,0))
      「(0,0,0)」の部分には座標が入ります。上記のは画面の中心にできます。
    3. オブジェクト選択: bpy.context.selected_objects ※配列になっている
    4. 編集モードに変換: bpy.ops.object.mode_set(mode='EDIT')
      オブジェクトモード: bpy.ops.object.mode_set(mode='OBJECT')
    5. モードの変換: bpy.ops.object.editmode_toggle(): 引数なし、編集モードとオブジェクトモードを切り替える

BMeshを使うサンプルコード

参考ページはこちらです。
bpy.bmeshモジュールは、blenderのbmeshデータへのアクセスを提供します。

BMeshとは

ジオメトリ接続データを備えたBlenderの内部メッシュ編集APIへのアクセスと、分割、分離、折りたたみ、
ディゾルブなどの編集操作へのアクセスを提供します。公開されている機能はCAPIに厳密に従っており、Blender独自のメッシュ編集ツールで使用される関数にPythonがアクセスできるようにします。

早い話が、「メッシュ」を操作するということです。

次はサンプルコードを実行してみましょう。

<サンプルコード>※参考ページにあったものです。

# This example assumes we have a mesh object selected

import bpy
import bmesh

# Get the active mesh
me = bpy.context.object.data

# Get a BMesh representation
bm = bmesh.new()   # create an empty BMesh
bm.from_mesh(me)   # fill it in from a Mesh

# Modify the BMesh, can do anything here...
for v in bm.verts:
    v.co.x += 1.0

# Finish up, write the bmesh back to the mesh
bm.to_mesh(me)
bm.free()  # free and prevent further access

Script画面を開いた状態で、下のように「新規」をクリックします。

そしたら、文字が入力できるようになるので、サンプルコードをコピペして貼り付けてみましょう。
※「コピペ」=文字を選択して「Ctrl + C」(コピー)してから「Ctrl + V」(ペースト)すること

貼り付けたら、右側にある「▶」ボタンを押してプログラムを実行します。
すると、初期表示していたCubeがX軸方向に移動します。

ちなみに、エラーメッセージなどを見るとプログラムの修正も楽です。
なので、ヘッダーメニューにある「ウィンドウ」->「システムコンソール切り替え」をクリックしてやるとよいです。

  • bmesh.types.BMesh.from_mesh: 既存のメッシュデータブロックからこのbmeshを初期化します。
    [from_mesh(メッシュ、face_normals = True、use_shape_key = False、shape_key_index = 0)]

    • パラメーター
    • mesh(Mesh)–ロードするメッシュデータ。
    • use_shape_key(boolean)–シェイプキーの場所を使用します。
    • shape_key_index(int)–使用するシェイプキーインデックス。
bm = bmesh.new()   # 空のBMeshを作成する
bm.from_mesh(me)   # 現在の編集モードから初期化する
  • bmesh.from_edit_mesh:
    me = bpy.context.object.data
    bpy.ops.object.mode_set(mode='EDIT') # エディットモードに変更
    bm = bmesh.from_edit_mesh(me)

このような形で、BMeshを取得します。そして、いろいろなコントロールを行うというわけです。

円を作成する

プログラム的には下のように処理を行うと作成することができました。

  1. データを書き込むためにプリミティブ(Cube)を作成
  2. BMeshを作成
  3. 円の作成(BMesh)
  4. 作成したBMeshをデータに書き込む -> これで画面上に表示される
  5. 作成したときにプリミティブの名前「Cube(立方体)」を変更
import bpy
import bmesh
import math
import mathutils

# 1. 立方体を作成
bpy.ops.mesh.primitive_cube_add(location=(0,0,0))
# 存在するオブジェクトのデータを取得
me = bpy.context.object.data

# 2. BMeshの作成
bm = bmesh.new()
# 3. 円の作成(BMesh)
# Add a circle XXX, should return all geometry created, not just verts.
bmesh.ops.create_circle(
    bm,
    cap_ends=False,
    radius=1.0,
    segments=8)
# 4. 作成したBMeshをデータに書き込む
bm.to_mesh(me)
bm.free()
# 5. 作成したときにプリミティブの名前「Cube(立方体)」を変更
for obj in bpy.data.objects:
    print(obj.name)
    if obj.name == 'Cube':
        print('change')
        obj.name = 'Test'

Pythonでの描画サンプルコード

ベジェ曲線のサンプルコード

. ペジェ曲線(Bezer Curv)を描く:

ops.curve.primitive_bezier_circle_add(radius=1.0,
                                      location=(0.0, 0.0, 0.0),
                                      enter_editmode=True)

参考サイト

from bpy import context, data, ops

# Create a bezier circle and enter edit mode.
ops.curve.primitive_bezier_circle_add(radius=1.0,
                                      location=(0.0, 0.0, 0.0),
                                      enter_editmode=True)

# Subdivide the curve by a number of cuts, giving the
# random vertex function more points to work with.
ops.curve.subdivide(number_cuts=16)

# Randomize the vertices of the bezier circle.
# offset [-inf .. inf], uniform [0.0 .. 1.0],
# normal [0.0 .. 1.0], RNG seed [0 .. 10000].
ops.transform.vertex_random(offset=1.0, uniform=0.1, normal=0.0, seed=0)

# Scale the curve while in edit mode.
ops.transform.resize(value=(2.0, 2.0, 3.0))

# Return to object mode.
ops.object.mode_set(mode='OBJECT')

オブジェクトの名前など一式

プログラムで、オブジェクトをコントロールするのに「名前」がわからないととても不便です。
なので、それを一覧化します。

  1. Structure
    Structure

    • Vertex: 点
    • Edge: 辺
    • Face: 面
    • Normals: 法線=何かに垂直な方向または線であり、通常は三角形またはサーフェスですが、線、曲線上の点の接線、または表面上の点の接平面を基準にすることもできます

ベストプラクティスで学習する

こちらにベストプラクティスのページがあります。

Blender Pythonはコードスタイルとして、PEP8を採用している。
大まかには下のようなところを気をつけましょう、というところでした。

  • クラス名のキャメルキャップ:MyClass
  • すべて小文字の下線で区切られたモジュール名:my_module
  • 4つのスペースのインデント(タブなし)
  • 演算子の周りのスペース:、ではありません   ◯「1 + 1」 X「1+1」
  • 明示的なインポートのみを使用します(ワイルドカードのインポートは使用しません*)
  • 1行で複数のステートメントを使用しないでください:、代わりに2行に分けてください。if val: body
  • 列挙型には一重引用符を使用し、文字列には二重引用符を使用します。
  • リストのコピーを避ける: 「新しいリストを返すよりも関数にリストを変更させる方が高速です。」

Blender Python APモジュール

  1. コンテキストアクセス(bpy.context)
  2. データアクセス
  3. メッセージパス
  4. 演算子
  5. タイプ
  6. ユーティリティ
  7. パスユーティリティ
  8. アプリケーションデータ
  9. プロパティ定義

コンテキストアクセス(bpy.context)

  • bpy.context.area
  • bpy.context.blend_data
  • bpy.types.BlendData、(読み取り専用)
  • bpy.context.collection
  • bpy.types.Collection、(読み取り専用)
  • bpy.context.engine
  • bpy.context.gizmo_group
  • bpy.types.GizmoGroup、(読み取り専用)
  • bpy.context.layer_collection
  • bpy.types.LayerCollection、(読み取り専用)
  • bpy.context.mode
    タイプ
    ['EDIT_MESH'、 'EDIT_CURVE'、 'EDIT_SURFACE'、 'EDIT_TEXT'、 'EDIT_ARMATURE'、 'EDIT_METABALL'、 'EDIT_LATTICE'、 'POSE'、 'SCULPT'、 'PAINT_WEIGHT'、 'PAINT_VERTEX'、 'PAINT_TEXTURE'の列挙型、 'PARTICLE'、 'OBJECT'、 'PAINT_GPENCIL'、 'EDIT_GPENCIL'、 'SCULPT_GPENCIL'、 'WEIGHT_GPENCIL'、 'VERTEX_GPENCIL']、デフォルト 'EDIT_MESH'、(読み取り専用)
  • bpy.context.preferences
  • bpy.types.Preferences、(読み取り専用)
  • bpy.context.region
  • bpy.types.Region、(読み取り専用)
  • bpy.context.region_data
  • bpy.types.RegionView3D、(読み取り専用)
  • bpy.context.scene
  • bpy.types.Scene、(読み取り専用)
  • bpy.context.screen
  • bpy.types.Screen、(読み取り専用)
  • bpy.context.space_data
  • bpy.types.Space、(読み取り専用)
  • bpy.context.tool_settings
  • bpy.types.ToolSettings、(読み取り専用)
  • bpy.context.view_layer
  • bpy.types.ViewLayer、(読み取り専用)
  • bpy.context.window
  • bpy.types.Window、(読み取り専用)
  • bpy.context.window_manager
  • bpy.types.WindowManager、(読み取り専用)
  • bpy.context.workspace
  • bpy.types.WorkSpace、(読み取り専用)

データアクセス(bpy.data)

Blenderの内部データへのアクセス、これはサンプルコードを参考にするようです。

import bpy

# print all objects
for obj in bpy.data.objects:
    print(obj.name)

# print all scene names in a list
print(bpy.data.scenes.keys())

# remove mesh Cube
if "Cube" in bpy.data.meshes:
    mesh = bpy.data.meshes["Cube"]
    print("removing mesh", mesh)
    bpy.data.meshes.remove(mesh)

# write images into a file next to the blend
import os
with open(os.path.splitext(bpy.data.filepath)[0] + ".txt", 'w') as fs:
    for image in bpy.data.images:
        fs.write("%s %d x %d\n" % (image.filepath, image.size[0], image.size[1]))

メッセージバス(bpy.msgbus)

BlenderデータブロックのプロパティがデータAPIを介して変更されたときに通知を受信できます。

詰まる所は、bpy.dataのオブジェクトに変更があった時、その変更されたことを受信(知ること)ができるということです。
<サンプルコード>※上記のリンク先にコードがあります。

アクティブオブジェクトの場所の変更に対するサブスクリプションの例

import bpy

# Any Python object can act as the subscription's owner.
owner = object()

subscribe_to = bpy.context.object.location

def msgbus_callback(*args):
    # This will print:
    # Something changed! (1, 2, 3)
    print("Something changed!", args)

bpy.msgbus.subscribe_rna(
    key=subscribe_to,
    owner=owner,
    args=(1, 2, 3),
    notify=msgbus_callback,
)

鍵になるのは下のコードです。
bpy.msgbus.subscribe_rna(キー、所有者、引数、通知、オプション= set())
パラメーター:

  • キー(マルチプル)–サブスクライブされているデータのタイプを表します
  • 所有者(任意のタイプ。)–このサブスクリプションのハンドル(IDで比較)。
  • オプション(文字列のセット)–
    サブスクライバーの動作を変更します。
    PERSISTENT 設定すると、IDデータを再マッピングするときにサブスクライバーが保持されます。

演算子(bpy.ops)

「演算子」と翻訳されているが、オペレーター(操作オブジェクト)と解釈した方がしっくりくるかもしれない。。。

大まかな機能としては、以下のものがあるようです。

  • 演算子の呼び出し:呼び出し元の演算子へのPythonアクセスを提供します。
       {'RUNNING_MODAL', 'CANCELLED', 'FINISHED', 'PASS_THROUGH'}{'FINISHED'}{'CANCELLED'} のみ使用可能
  • キーワードと位置引数: APIドキュメントにある順番で引数を渡す必要がある
    bpy.ops.test.operator(override_context, execution_context, undo) 1: override_context 2: execution_context 3: undo
  • コンテキストのオーバーライド(以下サンプルコード)
    # remove all objects in scene rather than the selected ones
    import bpy
    override = bpy.context.copy()
    override['selected_objects'] = list(bpy.context.scene.objects)
    bpy.ops.object.delete(override)

サブモジュール

アクション演算子 Anim演算子 アーマチュア演算子 資産オペレーター ボイド演算子
ブラシ演算子 ボタン演算子 キャッシュファイル演算子 カメラマン クリップ演算子
布演算子 収集オペレーター コンソールオペレーター 制約演算子 曲線演算子
サイクル演算子 Dpaint演算子 エドオペレーター Anim演算子のエクスポート
メッシュ演算子のエクスポート シーン演算子のエクスポート ファイル演算子 流体演算子
フォント演算子 ジオメトリ演算子 ギズモグループ演算子 Gpencil演算子 グラフ演算子
画像演算子 Anim演算子のインポート 曲線演算子のインポート メッシュ演算子のインポート シーン演算子のインポート
情報演算子 ラティス演算子 マーカー演算子 マスク演算子 マテリアルオペレーター
Mballオペレーター メッシュ演算子 Nla演算子 ノード演算子 オブジェクト演算子
アウトライナー演算子 ペイントオペレーター ペイントカーブ演算子 パレット演算子 粒子演算子
ポーズ演算子 Poselib演算子 設定演算子 Ptcache演算子 レンダリング演算子
リジッドボディ演算子 安全なエリアのオペレーター シーンオペレーター 画面演算子 スクリプト演算子
スカルプト演算子 シーケンサー演算子 サウンドオペレーター スプレッドシート演算子 表面演算子
テキスト演算子 テクスチャ演算子 変換演算子 UIオペレーター 紫外線オペレーター
View2D演算子 View3D演算子 Wm演算子 ワークスペース演算子 世界のオペレーター

現状はここまでです。

でわでわ。。。