前回までの「Python初体験」の続きです。

今回は文字列の解説です。

まず、大した話ではないのですが、Pythonでは""''がおんなじで、CやC++(C#も)が''で囲む「文字」と""で囲む「文字列」を峻別するので、それに慣れた私はちょっと吃驚しました。さらにまた、トリプルクオート("""または'''により、改行を含む複数行文字列を表すことが出来るそうです。
例えば、'ABC'"ABC"、さらに''ABC''""ABC""皆同じ文字列です。また、
'''ABC
DEF'''


"""ABC
DEF"""

は、"ABC\nDEF"と同じだそうです。

 

C系言語(をやってきた私にとっての最も注意すべきことですが)との大きな違いは、Pythonにはヌル文字がなく)、ヌル文字は単なる'\0'またはb'\0'の意味しかありません。
:「ヌル(null)文字

この理由は、そもそもPythonでは文字列が文字列長情報を持つバイナリーデータなので、ヌル文字による終端記号(Terminator-文字列の終わりを知らせるサイン)の必要がない訳です。()なお、文字列=""は空文、即ち長さ0の文字列データが「ある」-not None)です。
:バイナリーデータはエンコードの種類で異なります。Pythonの文字列デフォルトはUTF-8ですが、文字列.encode('shift_jis')とか文字列.encode(encoding='utf_16_le')で文字列データが変わります。なお、Copilot君によれば「Pythonの文字列は、内部的にはUnicode文字列として表現されます。Unicodeは、世界中のすべての文字を表現するための標準的な文字コードです。Pythonの文字列は、UTF-8エンコーディングでエンコードされたUnicode文字列として表現されます。UTF-8は、Unicode文字をバイト列に変換するための一般的なエンコーディング形式の1つです。」とのことです。またC系言語で終端記号は"NULL"や"null"などと書かれ、ASCIIでは1byteの\0、WindowsのUTF-16では2bytesの\0\0となります。

では、C系言語で変数の実体がないもの(例:文字列ポインターchar* ptr = null(0);→ポインター変数ptrはメモリーに存在するが、それが差し示す文字列はメモリーに無い。)のような「不存在(無い)」はどのように扱われるのでしょうか?PythonではそれをNoneといって、「不存在」を表すようです。

例えば、

fruits = {1:"apple", 2:"lemon", 3:"melon"}
print("fruisは3までしかありません。fruits.get[4]は", fruits.get(4), "となります。")

(出力)fruisは3までしかありません。fruits.get[4]は None となります。

dict = {"リンゴ":1, "レモン":2, "メロン":3}
print("辞書(dict)に「西瓜」は", dict.get("西瓜"), "です。")

(出力)辞書(dict)に「西瓜」は None です。

のように書くのだそうです。吃驚!

なお、条件式のis==の違いは「is演算子がオブジェクトの同一性を判定するのに対し、==演算子はオブジェクトの値の等価性を判定する」ということは、前回学習しましたね。
 

では、また次回。

 

↑のようなタイトルで釣ってしまって申し訳ありません。今年古希で現役の方のお楽しみのような話ではございません

 

ご存じの通り、2023年にC#を学習し、WinFormsからWPFまで来て、「(WPFで使えるコントロールを使う際等)書いて書けないことは無いけど、こりゃー、Visual Studioでないと難しいな。」という結論を得ました。しかし、「次のネタ」が思い浮かばずにPythonへ流れてゆきました。

 

それはそれで面白いのですが、Pythonの文法や構文を書いていると、矢張り書き慣れたC系、特にC++への郷愁が湧いてきて、

 

あ”~、久々に論理系のC++プログラム書きたいなぁ。

 

という思いが強くなります。

 

そんな時に、偶々私の雑多なWindowsプログラムのフォールダーにOpenGLのサンプル(注)が入っていて、

Q1:「OpenGLってなんだっけ?」

Q2:「Windowsでも動くんだっけ?なんかDLLが必要なんだっけ?」

Q3:「今でもOpenGLって使えるんだっけ?」

という疑問がわいたわけです。

注:ブログを書くために、C++のIDEの調査の為ダウンロードしたDev-C++のサンプルにあったWin32 SDKのプログラムです。

 

↑の答えは、次の通りのようです。

A1:OpenGL(by wiki)

A2:WindowsにおけるOpenGL(by Microsoft)

A3:シリコングラフィックスの2次元/3次元コンピュータグラフィックスワークステーション用APIが改良され、ライブラリとして公開され、多様な描画デバイスを包括するグラフィックスAPIのオープン標準規格として有名になりましたが、標準化の遅れやDirectXやAppleのMetal等、他のソフトウェアハウスのライブラリの発展もあり、Appleが2018年にOpenGL/OpenCLの非推奨化を発表したこともあり、順風とはいいがたい状況です。

 

じゃ、何故そんなものに興味を持つの?

 

といわれそうですが、まぁ、手っ取り早く、余計なコストや手間をかけずに2D/3DグラフィックがWindowsで書けるので、

 

CGドシロートの俺でも何かできるかな?

 

と思っただけです。(汗;)

 

今はまだそのDev-C++のサンプルから、OpenGLの古いバージョンでの私流のWin32 SDKプログラムへの移植を行った段階

ですが、これをC++のクラスとして簡単なカプセル化を行い、

 

趣味でプログラムする人が学習出来て、且つ簡単に3Dグラフィックのサンプルが書けるようになるといいな。

 

というノリで少し勉強してみようかな、と考えています。(まぁ、大したことはできないので期待しないでくださいね。)

 

ps. 勿論「Python日記」も並行してやってゆきますんで宜しく。

 

前回までの「Python初体験」は取り敢えずPythonを使ってみる、ということだったようで、これからソースコードの記述を行うプログラミングに進んでゆくようです。

このサイトではPythonの扱うと演算子を説明しますが、他の言語と異なっているところはハイライトにします。(同じく私が間違った所は太字ハイライトします。)

(1)特徴的な演算子
べき乗に'**'を使うところでしょうか?BASICでもC系言語でもべき乗は'^'を使うことが多いのでこれはちょっと違いますね。なお、「余り」はC系言語と同じく'%'(モジュロ)演算子です。(ご参考:BASICは'MOD')

(2)変数型の強制変換
C系言語では、変数の型宣言に厳しいですが異なる型の演算はキャスト(強制型変換)が無ければくてもコンパイルエラーになりますませんが、これもPythonで自動的に型変換してくれるようです。(注)

注:Windowsプログラミングで型違いエラーを多く経験したので間違えました。整数、単精度実数、倍精度実数の代入や算術演算の場合は黙示の強制変換が行われます。(参考
<C言語の例>
int a;
float b, c;

c = b + (float)a;    //明示的型変換しかしキャストを外してもエラーは起きない。

c = b + a;             //こうしても黙示的に型変換が行われる
a = b + c;             //こうしても黙示的に型変換が行われる

//解説:スミマセン。いつの間にか忘れていました。しかし、明示的型変換で対応していないと、しつこいバグに悩まされる可能性があります。

<BASIC言語の例>
Dim a as Integer
Dim b as Single, c as Single

c = b + a(注)
注:整数、単精度、倍精度浮動小数点変数の混合計算の演算結果は、自動的により変数のバイト数(整数 < 単精度 < 倍精度浮動)が多い型に変換される。

<Pythonの例-上段の実行結果が下段>
10(整数)+ 0.1(浮動小数点実数)
10.1(演算結果は浮動小数点実数)

また、Pythonでは除算は整数で割り切れても浮動小数点実数になるようです。
6 / 2(整数と整数)
3.0(実数)

(3)整数除算
整数の除算では解は整数になることから、剰余は切り捨てられます(例:"3 / 2"の演算結果は1になる。)が、Pythonでは演算結果は自動強制型変換により1.5(浮動小数点実数)になってしまいます。
Pythonでは、整数演算をしたい場合には整数除算演算子である'//'を使います。これは浮動小数点実数に対しても有効です。
<例>
3 / 2
1.5


3.0 // 2.0(値は浮動小数点実数ですが、整数除算演算子があると強制的に整数に変換されるようです。)
1("3.0 / 2.0"の場合は1.5となる。)

(4)論理演算子
論理演算子として次の3つがあるそうです。
and 演算子
or 演算子
not 演算子
C系の言語だと&&、||、!で、BASIC/Visual BasicだとAND/And、OR/Or、NOT/Notなので、似ていますね。

(5)ビット演算子
& →論理積
| →論理和
^ →排他的論理和
~ →反転
<<, >> →シフト
C系の言語と同じですね。

(6)比較演算子
x == y       x と y が等しい        #C++等と同じですね。
X != y       x と y が等しくない    #C++等と同じですね。
x > y        x は y よりも大きい
x < y        x は y よりも小さい
x >= y       x は y と等しいか大きい
x <= y       x は y と等しいか小さい
x in y       x という要素 が y に存在する    #Python独特
x not in y   x という要素 が y に存在しない  #Python独特

なお、予約語'False'('false'だとエラーになる)は0と等価です。(因みにprint(True == 1)も試しましたが、Trueでした。)
print(False == 0.0)

この他"is"という演算子があり、これは値ではなく、オブジェクトが同一か否かを調べるとのことです。実際にColabでやってみると、
a = 100
b = 150
c = 100
print("a == b ->", a == b)
print("a == c ->", a == c)
print("a's ID->", id(a), "b's ID->", id(b))
print("a is b ->", a is b)
print("a's ID->", id(a), "c's ID->", id(c))
print("a is c ->", a is c)
c = b  #値もIDも違う
print("a’s ID->", id(a), "c's ID->", id(c))
print("a is c ->", a is c)

結果は、次のようになります。
a == b -> False
a == c -> True
a's ID-> 137388688837968 b's ID-> 137388688839568
a is b -> False
a's ID-> 137388688837968 c's ID-> 137388688837968
a is c -> True
a's ID-> 137388688837968 c's ID-> 137388688839568
a is c -> False  #値もIDも違う

変数オブジェクトのaとcは違うのですが、値100のメモリー位置が同じなのでしょうか?"a is c"はTrueになります。

"c = b"の所を変えて、"c = b * 2 / 3"(値は同じ)にすると、結果は、
a == b -> False
a == c -> True
a's ID-> 137388688837968 b's ID-> 137388688839568
a is b -> False
a's ID-> 137388688837968 c's ID-> 137388688837968
a is c -> True
a's ID-> 137388688837968 c's ID-> 137387420507568
a is c -> False  #値は同じだが、IDは違う

になりました。これは値のオブジェクトが変わったからでしょう。(
:Webにこの問題に関連するPython文書の引用がありましたのでご参考まで。

注意点: イミュータブルな型の同一性比較
    整数intや文字列strなどのイミュータブル(変更不可)な型のオブジェクトに対するisやis notでの比較は注意が必要。
    新たにオブジェクトを生成したときに、既存のオブジェクトへの参照が返される場合とそうでない場合がある。
    例えばintの場合、-5から256の範囲の値を生成すると既存のオブジェクトへの参照が返されるが、その範囲外の値は別のオブジェクトとして生成される。
    The current implementation keeps an array of integer objects for all integers between -5 and 256. When you create an int in that range you actually just get back a reference to the existing object.
    整数型オブジェクト (integer object) ? Python 3.11.3 ドキュメント

    a = 256
    b = 256
    print(a is b)
    # True

    a = 257
    b = 257
    print(a is b)
    # False


なかなか違う言語の学習は面白いですね。

 

前回「Python初体験」というご本家Pythonサイトの教育シリーズを覗いた話をしました。

今回のPythonのサンプルプログラムは次の通りです。

###\
import matplotlib.pyplot as plt  #解説:matplotlibモジュールのpyplotクラスインスタンスをpltという名前で使います、という意味か?

x_values = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June']  #解説:これが文字列配列と初期化です。
y_values = [100, 130, 80, 150, 140, 130] #解説:これが整数配列と初期化です。(浮動小数点にするには".0"が必要)

plt.bar(x_values, y_values)  #解説:相対するx(横)、y(縦)を使って某(baar)グラフを書く(準備-初期化)ようです。
plt.plot()  #解説:実際にグラフを描くのはここのようです。
plt.show()  #解説:実際に表示するのはここのようです。\

"""

前にお話しした通り、ライブラリ(WindouwsのDLL)を読み込んで使う"import"で"matplotlib"を読み込んで、文字列配列のx_valueと整数配列のy_valueを作ります。
そして読み込んだライブラリの「pltという(as plt)」インスタンスを使って、それら配列を喰わせて、plot()します。
:動詞のplotは「企む」(サスペンス小説等に「プロット」、よくありますね)の他、「〈土地を〉区分する,区画する」「<土地・建物などの〉図面を作る」という意味があります。なお、作っただけでは分からないので表示(show)します。

ここまでの印象だと、PythonはBegin~Endでブロックを作るPascalなどのAlgol系言語や、{}でブロックを作るC系言語とは異なり、型宣言が緩るいまたは無いBASIC言語的フレーバーですが、ライブラリーはC言語のように関数で呼び出すようです。また、インタープリター()というよりも、ライブラリーの(Colabだからダウンロードして)読み込みに時間がかかるのか、新しいプログラムの実行にはやや時間がかかりますね。

参考:プログラミング言語の比較
 

:よくある説明でスミマセン。

コンパイラー→ソースコードを一括してCPUが実行可能な機械語コードに変換して、それを実行する。

インタープリター→ソースコードの実行単位毎に解釈してインタープリターが逐次実行してゆく。

 

因みにPythonはインタープリターだと言われていますが、Copilot君にコンパイラーは無いの?と聞くとこのような返答でした。

Pythonにおけるコンパイルとは、高級言語で書かれたプログラムを機械語に変換するプロセスです。このプロセスはコンパイラによって実行され、エラーのチェックや最適化が行われます。例えば、C言語のソースコードをコンパイルすると、実行可能なバイナリファイルが生成されます.

Pythonは一般的にはインタプリタ型の言語として知られていますが、実際にはPythonのソースコードは実行前に**「バイトコード」**と呼ばれる中間的なコードにコンパイルされます。このバイトコードはPythonのインタプリタによって実行されています.

Pythonとコンパイルは密接な関連性を持っており、バイトコードによってプログラムの実行速度が向上し、ハードウェアリソースを効率的に利用できるようになります。また、バイトコードはソースコードを直接公開せずに配布することができ、ソースコードの保護にも役立ちます.

Pythonの標準ライブラリには「compileall」というモジュールがあります。これは指定されたディレクトリ内のすべてのPythonソースファイルをバイトコードにコンパイルするためのツールです。例えば、以下のように使用します:

 

import compileall

compileall.compile_dir('<directory_path>', force=True)

 

このコードは指定されたディレクトリ内の全てのPythonファイルを強制的にバイトコードにコンパイルします.

Pythonのバイトコンパイルは、Pythonのソースコードをバイトコードに変換するプロセスであり、Pythonインタプリタが直接実行できる低レベルのプラットフォーム非依存のコードを生成します.

 

前にwebで調べた「Python入門物」のサイトがローカル開発環境を扱っていたので、Google Colabベースの教育モノをググってみた所、ご本家に「Python初体験」というサイトがあることが分かりました。

そして今日実行したサンプルプログラムは以下の通り。サイトにあったロゴと現れたロゴは違いますが、まっ、いーか?

# Pythonにあったサンプル  #解説:Pythonはコメント記号が'#'です。
import io
import requests
import PIL.Image


response = requests.get("https://www.python.jp/logo.png")  #解説:URLパスからpngファイルを取得
PIL.Image.open(io.BytesIO(response.content))  #解説:恐らくイメージファイルデータをバイトストリームにして開いた?

序に次のリス(栗鼠-Squirrel-「スクゥァーレル」と「スクゥォーレル」の間っぽい)の映像も。
response = requests.get("https://www.python.jp/sampledata/Japanese_Squirrel_WUXGA.jpg")
PIL.Image.open(io.BytesIO(response.content))  #解説:一つの出力ウィンドウには一つのコンテントしか出せません。

未だ説明はありませんが、最初の"import"はC#でいうところの"using"と同じで、DLLのアップロードと読み込みなんでしょう。ioは基本のinput-outputシステム、requests.getはファイルデータ取得要求かな?そしてイメージデータの場合そのファイル操作がPIL.Imageで、このデータをopenしたということなんでしょう。

今日はこんなところで。

Webで一応Googleが運営するColaboratoryの導入に目を通しましたが、先ず用語やシステム環境の概念()が、苦手なWebベースの話なのでまだしっくりときません。とはいえ、「プログラムをダウンロードしてインストール、設定してから実行」というのではなく、WebでGoogle Colaboratoryに行き、Notebookを作成して(昔のROM Basicのダイレクトモードのように)コマンドを入力するだけらしいので、まずそれをやってみるか?と考えました。
:TPUとかGPU(TPUってポリウレタン系熱可塑性エラストマー???GPUもどうもハードウェアのGraphic Processing Unitという意味ではなさそうで、Googleのクラウドサーバーにある高速プロセッサーのようです...後日譚TPUは分かりました。又GPUは本当にNVIDIAのGPUらしい。)とか、まだ分からない。おいおい調べてゆきましょう。

ということで「見るよりも飛べ」という箴言に従い、Google Colaboratory("Colab")に飛んでまずはサンプルのノートブックを作成しました。

入力セルに、

print("Hello, Python!")

と入力、左の実行ボタンを押して、

Hello, Python!

という実行結果を確認するだけ。

ウ~ン、虚しいっ!ということで色々と悪戯を。



ps. Pythonは元々オープンな環境でうまれた、ありがたい開発システムですが、それまでのユーザーの蓄積がライブラリーとして惜しげなく提供されているという柔軟で発展性のあるもの、というのが特徴であり強い印象でもあります。(まだ、全然わかっていませんが。)

C++用のリソースエディター、開発ツールとライブラリーである(2002年に初公表した)BCCForm and BCCSkeltonのサポート・解説で始まったこのブログ。BCCSkeltonのCOM対応、Unicode対応(ECCSkelton)を経て、MSCompAss.exeの開発を機にC#に流れて行き、C#もWinFormsからWPF迄来たのですが、どうもそれでもネタ切れ感が強くなってきました。

一応【無駄話】や【食い物話】を挿入して続けていますが、もう一つ大きな柱、それも「プログラミング関連で欲しいな」と感じ、(2023年にC#を学習したように)2024年のプロジェクトを物色してきました。その結果、矢張りプログラミングブログだから「新しい(流行りの)言語をやろう」ということで、私は全くの門外漢ですが昨年GAI(General Artificial Intelligence)の話も取り上げたことから、

に取り組んでみようと思います。

と、

そこまで決心したのですが、一から始めるので初心者向けのサイトをつらつらと見て行き、Pythonの公式サイトに行って、Pythonの無料ダウンロードが可能であることを知りました。しかし、Pythonの開発環境を構築するには更にAnacondaを導入する必要があるようです。(注)
注:Copilot君曰く、「Anacondaとは、Pythonのディストリビューションで、Pythonの開発環境を一括でインストールすることができるディストリビューションです。Anacondaには、Pythonの開発に必要なライブラリやツールが含まれており、Pythonの開発環境を手軽に構築することができます。」

↑のPythonAnacondaの説明を読まれた方は、(私の様に、書かれている多くの言葉の意味を)絶望的に理解できないことを嘆くでしょうし、私のPCは500GBのSSDしかなく、その他C++やC#等の開発環境や、ビデオ、音楽が結構メモリーを占有していて、(ダウンロードしても使いこなせないような大きなファイルを落とすには)「これは大きいな、ウ~ン()」と悩み、手を出すのをためらってしまいました。
:Pythonだけなら100MB程度なのだそうですが、Pythonを使うならAIやデータ分析を快適な環境で行いたいのでAnaconda(6GB程度)は不可欠のように感じます。

そんなところにGoogleが運営するColaboratoryというwebベースの開発環境()が提供されていることを知り、「まずはこれでPythonを試し(に学習し)、更なるステップアップが必要ならその時にAnacondaを入れればよいか?」と考え始めました。
:「データサイエンス」や「機械学習」も試すことが出来そうなことを書いています。

ということで、

全く白紙の状態でPythonを学習する私の体験談として、新たに【Python日記】シリーズを始め、気が付いたこと、印象的なこと、失敗談などを書いてみようかと思います。

 

ps. 「Python日記」は飽くまで一つの記事シリーズなので、今後もネタがあればBCCForm and BCCSkelton、C++、C#のプログラミングのみならず、【無駄話】、【食い物話】を続けてゆくつもりです。

 

昨年末に始めた本シリーズ。C#が昔のCOMサービスをどう扱っているのか知りたくて、調べるうちにWinFormsとラッパーではなく、WPF用のUIElementであるMediaElement/MediaPlayerや、SpeechSynthesizer/SpeechRecognitionEngineを使う為にWPFでのプログラミングを学習してきました。

 

が、

 

矢張り結論は

【WPF】まずは結論から...

変わりません。

 

Webで見ると、WinFormsは旧いWinAPIプラットフォームに使っていますが、プログラムサイズが小さいとか利点もあるようですが、矢張りWin32APIのプラットフォームはこれから学ぶ/使うにはお勧めはできないかもしれませんね。矢張り比較的新しい技術(DirectXやWeb/Xaml)に基づくWPFの方が寿命が長いでしょうし、描画系実行速度やベクター画像が使える利点は大きいです。一方、WinFormsはC#コードだけですべてが記述できるという(旧いプログラマーにとっての)良さがありますが、WPFは(UIと処理コードを分離開発するという現代で)UIElementをXamlで記述することを前提にしているので、(私がやったようにC#のみで何とか簡単なものは書けますが)Visual Studioを使わないと現実的ではないと思います。(まぁ、こういうこともありますが...)

 

しかし、

 

WinForms、WPFまたはWinUI 3だなんだ、といっても所詮はユーザーインターフェースの世界であり、C++、C#だろうがどんな言語だろうが、ロジックを記述するだけの道具でしかなく、

 

何を作るか、というコンテンツ

 

がなければ、「(道具を撫でているだけで)プログラミングにはならない」ということを改めて痛感した次第です。(注)

注:【WPF】シリーズで私が作ったものは、ウィンドウやコントロールとその使い方の紹介以上のものはない、です。

 

内容の面白いコンテンツはWinFormsで作ろうが、WPFで作ろうが、WinUI #で作ろうが面白いでしょう。プラットフォームの違いは最新のハードウェアの能力(演算、描画や速度等)を引き出すだけの違いでしかないわけです。(注)

注:昔、SONYがPSで画像の微細性で圧倒的に強かった時も、任天堂はコンテンツの面白さにこだわったことが思い出されます。

 

素人の趣味プログラマーにとっては結局、

 

ネタ探しの旅は終わらない

 

ということにつきますか?

 

しかし、

プログラミング言語の違いにより、作るものに違いが出てくるようなパラダイム変化があり得るものだろうか?

という問いはそれ自体で意味があるのかもしれません。これはネタとして使えるかもしれませんね。

 

BCCSkeltonでもやったTextToSpeech、DirectShowなどはどんな形でC#で使われているのだろう?」という疑問から昨年末から続いてきた【WPF】と【SAPI】シリーズですが、今回で一応お開きということになります。おかげさまで(知らずに使っていた)WinFormsとは異なるUIプラットフォームのWPF(従って、XamlやMSBuild、UIElementやContent等周辺知識も含めて)や現在のC#における音声合成・認識について「一舐め」させていただいた気分です。

 

さて音声認識については、BCCSkeltonでプログラミングをしていた時からSAPI 5で音声合成と一緒に提供されていたことは知っていました。しかしこの機能を必要としなかったので、好奇心からこのサンプルをコンパイルしましたが、認識精度が低いので興味を失っていました。(マイクが遠かったり、その精度も影響していたかも?)

 

今日日(キョウビ)の音声認識はもっとスマートですし、精度も高いみたいですね。以下のプログラムはWebで拾ったサンプルを合成し、WPFでウィンドウプログラム(注)に改造したものです。マイクが近いか、遠いかでも認識精度が大きく違うみたいです。

注:今回はまだ使ったことのなかったDockPanelを使ってみました。これを使うと上下左右に振り分けが可能になります。ただし、「下(Bottom)」を選択すると、更に左右に指定することはできないようです。

 

先ず、インストールされている音声認識エンジンを確認し、どれを使うか聞いてきます。(選択しないとずーーーーーっとこのループを繰り返します。プログラムをキャンセルで終了してもよいです。)

その後はマイクに向かって(近づけた方がよく認識されます)話しかけてください。文法に沿って文章を推測し、その後確定(認識)します。いっぱい表示される推定文がうざい、と感じたら「推定不要」ボタンを押してください。認識された分だけだとプロセスが分からない、という場合は「推定必要」ボタンを押しましょう。

 

【SpeechRecognitionWin.cs】

///////////////////////////////////////////////
//SpeechRecognitionEngineのテスト-Windows版
///////////////////////////////////////////////

using System;
using System.Windows;
using System.Windows.Controls;        //コントロールを利用する為
//using System.Windows.Media.Imaging;    //BitmapFrameを使用する為(解説:今回はプログラムアイコンを使用)
using System.Speech.Recognition;    //音声認識エンジン-System.Speech.dll

namespace SpeechRecognitionWin
{
    ///////////////////////////
    //エントリーポイントクラス
    ///////////////////////////

    class MainApp
    {
        [STAThread]
        public static void Main()
        {
            MainWindow mwnd = new MainWindow();
            Application ap = new Application();    //解説:WPFでは明示的にインスタンスを作る必要がある
            ap.Run(mwnd);
        }
    }

    public partial class MainWindow : Window
    {
        //MainWindowクラスの変数
        SpeechRecognitionEngine recEng;    //音声認識エンジン
        TextBox TBox;                    //テキストボックス
        ListBox LBox;                    //リストボックス
        Button extBtn, hypoBtn;            //終了、推定表示可否ボタン
        bool AddHypo = true;            //推定処理の表示フラグ

        //コンストラクター
        public MainWindow()
        {
            //this.Icon = BitmapFrame.Create(new Uri("Icon.ico", UriKind.Relative));    //同じフォールダーのIcon.icoを読んで使用する(解説:今回はプログラムアイコンを使います。)
            this.WindowStyle = WindowStyle.ThreeDBorderWindow;
            this.Background = SystemColors.WindowBrush;
            this.WindowStartupLocation = WindowStartupLocation.CenterScreen;
            this.Width = 525;
            this.Height = 350;
            this.ResizeMode = ResizeMode.CanResizeWithGrip;
            this.ShowActivated = true;                            //アクティブ状態で初期表示するかどうか-規定値 true
            this.ShowInTaskbar = true;                            //タスクバ-にボタン表示するか否か
            this.Title = "Speech Recognition";
            this.Loaded += Window_Loaded;
        }
        
        //WM_CREATE時処理
        private void Window_Loaded(object sender, EventArgs e)
        {
            //コントロールの設定
            InitControls();
/*解説: 音声認識を行うには、

 (1)音声認識エンジンのインスタンスを作成する

 (2)ディクテーション用辞書を作成する

 (3)

*/

           //解説:インストールされていないと致命的エラーになりますが、Windowsのロケールのエンジンはあると思います。
           //インストールされている音声認識エンジンでインスタンスを生成

            recEng = new SpeechRecognitionEngine(SelectEngine());
            //DictationGrammar (ディクティション用の文法)の追加
            recEng.LoadGrammar(new DictationGrammar());
            try
            {
                //音声認識エンジンへの入力設定(解説:マイクなどの入力デバイスがないと致命的エラーとなります。)
                recEng.SetInputToDefaultAudioDevice();
                //非同期音声認識を継続、複数音声認識を実行する

//解説:非同期音声認識操作複数音声認識(一回だけで終了しない)を参照してください。

                recEng.RecognizeAsync(RecognizeMode.Multiple);
            }
            catch
            {
                MessageBox.Show("音声標準出力がありません", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            //音声認識イベントハンドラー
            recEng.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(recEng_SpeechRecognized);
            //recEng.SpeechRecognized += recEng_SpeechRecognized;    //解説:表示方法が異なるだけで↑と同じ。
            //音声推定イベントハンドラー
            recEng.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(recEng_SpeechHypothesized);
            //recEng.SpeechHypothesized += recEng_SpeechHypothesized;    //解説:表示方法が異なるだけで↑と同じ。
        }

        //インストールされた音声認識エンジンを表示し、選択させる
        public RecognizerInfo SelectEngine()
        {
            //取り敢えず、インストールされた全音声認識エンジンをリストボックスに表示
            foreach(RecognizerInfo ri in SpeechRecognitionEngine.InstalledRecognizers())
            {
                LBox.Items.Add(ri.Culture.Name);
            }
            //音声認識エンジンの選択
            RecognizerInfo info = null;
            while(info == null)    //選択しない限り終了しない
            {
                //インストールされた音声認識エンジンを順に表示
                foreach(RecognizerInfo ri in SpeechRecognitionEngine.InstalledRecognizers())
                {
                    MessageBoxResult result = MessageBox.Show(
                                    "以下のエンジンが見つかりました。\r\n\r\n" + 
                                    ri.Name + " : " + ri.Culture.Name + 
                                    "\r\n\r\nこれを選択しますか?(キャンセル-終了)", "音声認識エンジン", 
                                    MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
                    if(result == MessageBoxResult.Yes)    //選択
                    {
                        info = ri;
                        MessageBox.Show(ri.Culture.Name + " で音声認識を開始します。", "通知", MessageBoxButton.OK, MessageBoxImage.Information);
                        break;
                    }
                    else if(result == MessageBoxResult.Cancel)    //キャンセルは終了
                    {
                        Close();
                    }
                }
            }
            return info;
        }

        //コントロールの設定
        public void InitControls()
        {
            //グリッド(Grid)を作成
            Grid grid = new Grid();
            //縦二列を作成・定義
            ColumnDefinition colL = new ColumnDefinition();
            ColumnDefinition colR = new ColumnDefinition();
            colL.Width = new GridLength(this.Width / 2, GridUnitType.Pixel);
            //colL.Width = GridLength.Auto;
            colR.Width = GridLength.Auto;
            grid.ColumnDefinitions.Add(colL);
            grid.ColumnDefinitions.Add(colR);
            //テキストボックスを作成
            TBox = new TextBox();
            //位置、フォントサイズとその他設定
            TBox.Margin = new Thickness(5, 5, 5, 5);
            TBox.FontSize = 12;
            TBox.HorizontalAlignment = HorizontalAlignment.Left;
            TBox.TextWrapping = TextWrapping.NoWrap;
            TBox.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
            TBox.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
            //グリッド左列にをテキストボックスを登録
            Grid.SetColumn(TBox, 0);        //Gridクラスの静的メソッドである点に注意
            grid.Children.Add(TBox);
            //ドックパネル(DockPanel)を作成

            //解説:今回はまだ使ったことのなかったDockPanelにしてみました。
            DockPanel dPanel = new DockPanel();
            //ラベルを生成
            Label lbl = new Label();
            lbl.Content = "このPCにインストールされている音声認識エンジン";
            lbl.HorizontalAlignment = HorizontalAlignment.Center;
            DockPanel.SetDock(lbl, Dock.Top);    //DockPanelクラスの静的メソッドである点に注意
            dPanel.Children.Add(lbl);
            //リストボックスを生成(解説:これも使ったことが無かったので無理矢理使ってみました。)
            LBox = new ListBox();
            LBox.SelectionMode = SelectionMode.Single;
            LBox.Margin = new Thickness(10, 10, 10, 10);
            LBox.HorizontalAlignment = HorizontalAlignment.Stretch;    //解説:伸びることは無いですね。
            LBox.VerticalAlignment = VerticalAlignment.Stretch;
            DockPanel.SetDock (LBox, Dock.Top);    //DockPanelクラスの静的メソッドである点に注意
            dPanel.Children.Add(LBox);
            //終了ボタンを生成(解説:「縦の底」を選ぶと下から挿入します。)
            extBtn = new Button();
            extBtn.Width = 60;
            extBtn.Height = 24;
            extBtn.Margin = new Thickness(10, 10, 10, 10);    //構造体 Thickness(左, 上, 右, 下)
            extBtn.HorizontalAlignment = HorizontalAlignment.Stretch;    //解説:結局中央(Center)になります。
            extBtn.VerticalAlignment = VerticalAlignment.Bottom;    //解説:「縦の底」
            extBtn.Content = "終了";
            extBtn.Click += extBtn_Click;
            DockPanel.SetDock (extBtn, Dock.Bottom);    //DockPanelクラスの静的メソッドである点に注意
            dPanel.Children.Add(extBtn);
            //推定ボタンを生成
            hypoBtn = new Button();
            hypoBtn.Width = 60;
            hypoBtn.Height = 24;
            hypoBtn.Margin = new Thickness(10, 10, 10, 10);    //構造体 Thickness(左, 上, 右, 下)
            hypoBtn.HorizontalAlignment = HorizontalAlignment.Stretch;    //解説:結局中央(Center)になります。
            hypoBtn.VerticalAlignment = VerticalAlignment.Bottom;
            hypoBtn.Content = "推定不要";    //解説:初期値(AddHypoが真の場合)
            hypoBtn.Click += hypoBtn_Click;
            DockPanel.SetDock (hypoBtn, Dock.Bottom);    //DockPanelクラスの静的メソッドである点に注意
            dPanel.Children.Add(hypoBtn);
            //グリッド右列にドックパネルを登録
            Grid.SetColumn(dPanel, 1);            //Gridクラスの静的メソッドである点に注意
            grid.Children.Add(dPanel);
            //MainWindowにgridを追加
            this.Content = grid;
        }

        //終了処理
        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            //終了時に音声認識エンジンを開放する。

            //解説:一応はガーベージコレクターが解放するようですが、「注意」に明示的開放が必要とあります?
            recEng.Dispose();
        }

        //認識時の処理
        void recEng_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            this.TBox.Text = "認識:" + e.Result.Text + "(" + e.Result.Confidence + ")\r\n" + this.TBox.Text;
        }

        //推定時の処理
        void recEng_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            if(AddHypo)    //解説:AddHypoが真の際に推定値を表示する
                this.TBox.Text = "推定:" + e.Result.Text + "("+ e.Result.Confidence + ")\r\n" + this.TBox.Text;
        }

        //推定ボタンクリック時の処理
        void hypoBtn_Click(object sender, RoutedEventArgs e)
        {
            AddHypo = !AddHypo;    //解説:所謂トグル処理
            if(AddHypo)
                hypoBtn.Content = "推定不要";
            else
                hypoBtn.Content = "推定必要";
        }

        //終了ボタンクリック時の処理
        void extBtn_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }
    }
}

 

今回もWPFでウィンドウプログラムを書きましたが、次は【WPF】シリーズの総括をしようと思っています。

 

昔(2020年頃?)Visual StudioをC++とC#でダウンロードしましたが、C#で新規プロジェクトを作ろうとして、(当時は基本的な知識が不足していたことが最大の理由でしたが)余りの選択肢の多さや、操作系の複雑さから「無理っ!」と断念しました。

 

その後、旧いバージョンのC#コンパイラーがWindows 10/11に標準装備されていることを知り、自身のBCCSkeltonで簡単なツール(MSCompAss.exe)を作ったことから、2023年にC#を(WinFormsで)学習して、(Xaml無しですが)WPF迄やり、今日まで来ました。

 

本日、私の旧機(32bit Windows 10)のバックアップを取った際に、不図(そのまま放っておいた)Visual Studioを起動、更新し、「C#、WPF、Windows デスクトップ」で「新規プロジェクト」をポチっとしてみました。(注)その理由は「ここまで学習したのだから、今はスイスイと使えるだろう」と考えたからです。

注:どうでもいいですが、このウィザード風ダイアログはWPFで作られていますね、当たり前ですが。

 

自動作成されたアプリケーション(プロジェクト)を取り敢えずビルドしてみると、ウィンドウ一枚が現れます。それだけでは余りにかわいそうなので、「終了ボタン」位は付けようと思いましたが、どのようにしたら「Xamlデザイナー(注)」と「Xamlコード」の画面を開けるのか分かりません。

注:大体、何が「Xamlデザイナー」なのか(WYSWYGのデザイン画面や、Xamlのコードエディター画面)分かっていませんでした。これも今日Microsoftで調べ、Visual Studioのタブでvamlファイルを開くと「(ビジュアル)デザイン(ビュー)Xaml(コードビュー)を持つタブ画面」が表示され、それ全体を「Xamlデザイナー」と呼ぶようです。(知らんけど。)

 

結局、「ソリューションエクスプローラー」の"MainWindow.xaml"(ファイル)をダブルクリックすると、Xamlデザイナーが立ち上がることが分かりました。

 

ボタンを追加するには「Visual Studioの左端のタブの『ツールボックス』をクリックし、デザイン(ビュー)で矩形を作り、表示されたボタンをドラッグしたり、サイズを変更したりする」ことで作成することは、前にWebの記事で読んだことがあり、スムースにできました。勿論、Xaml(コードビュー)に直接書き込んでも、両方のビューで同期していますので直ぐにデザイン(ビュー)に現れます。削除や変更も同じです。ということでボタンを一つ追加しました。(注)

注:↑の画面の追加された"Exit"ボタン(インスタンス名:"button")に、(特にその位置に)注意してください。

 

さて、ではこのボタンにコードビハインドでウィンドウ終了("Close();")処理を書き込もうと思いましたが、またどうすべきかが分からなくなりました。(注)

注:BCB(Borland C++ Builder)の場合はイベントテーブルがフローティングウィンドウであり、それをクリックすればよいだけでしたが...。

 

結局、Webでググって方法を確認し、「Visual Studio右下の『プロパティ』ウィンドウ」(確かにこのボタンのプロパティが表示されています)

の「右上のレンチアイコンの右の稲妻アイコン」をクリックするとイベントウィンドウが現れるようです。

確かに。(しかし、イベントテーブルを表示するなら、ウィンドウタイトルを「プロパティ」から「イベント」に変ろよな、とか、いっそ「プロパティ」に選択状態を表示し(例:「プロパティ-Button: bottun」)、このウィンドウをタブ切替にしてくれれば、まだ分かりやすかったよなぁ、と突っ込みを入れたくなります。)

 

いずれにしても↑で表示されているように、"Click"欄をダブルクリックしたら、このButtonクラスインスタンスの"button"君のOnClickイベントメソッド(button_Click)が"MainWindow.xaml.cs"ファイルに追加され、

 

private void button_Click(object sender, RoutedEventArgs e)

{

    //解説:やっとここに、Close();を書き込むことが出来ます。

    //(省略しないで書くと、MainWindow(thisのメソッドである this.Close();)

}

 

やっとウィンドウを閉じるボタン処理ができました。(疲れたなぁ。)

 

と、いうことで、

 

早速実行!

 

んんんんん?

 

 

Xamlデザイナーの表示位置と違うじゃん!!!(より下に位置されています。)

 

ということで、またまた、更に私のVisual Studio嫌いが増幅されました。(何か私の知らないことで、「私が操作を誤った」ということなんでしょうが、初心者に責を押し付けるのはやめてほしいよな。Visual Studio初心者版、中級者版、プロ版なんていうVersionないかしら?)

 

ps. あと一つ。↑のイメージの通り、プロパティウィンドウの右下に「ソース管理に追加」と書かれています。これはメニューバーにある"Git"に対応しているようであり、このGitとはプロジェクトを複数人で行う際にファイルの履歴管理が面倒になることのソリューションとして開発された「フリー且つオープンソースの分散型バージョン管理システム」のようです。(GitHubはGitそのものではなく、GitHubとは、Gitの仕組みを利用したウェブ上のバージョン管理サービスのようです。)

あー、世の中変わっちゃったよな。