=[広告]====================================================================== Webサイトを作ったけど人が来ない,人は来るけど売れないとお困りの オンラインショップ管理者様,趣味のページやメルマガで広告収入がほしい, サイトの更新するのが面倒くさい,アフィリエイトを試してみたけど効果が なかった,もっといろんな人に見てほしいという個人サイトオーナー様, 在庫も発送の手間もなしでWebショップを持ちたいとお考えの初心者様, そんな方々にお勧めの,正統派の技術を教えてくれる講座です。 http://www.my-ir.com/wkl/ 今ならギフトナンバー「w983」で10%OFFサービス中! ============================================================================= ★☆=====================================================================☆★ 【wxWindowsでGUIプログラミング】 第7号: 調子笛ソフト改造(1) 2003.10.19(Sun) ★☆=====================================================================☆★  こんにちは,二条です。  今日は情報処理技術者試験の日だったらしいですね。実は,私はこの業界にいて,まだ一度も受験したことがないのですが,知人の中には大騒ぎしている人も多数いました。  今回の内容は,ワンポイント解説のコーナーのみ,前回までの内容を前提として進めますので,初購読の方はお手数ですが,バックナンバーを参考にしてくださいませ。  バックナンバーは,まぐまぐのサイト http://www.mag2.com/m/0000108320.htm で見ることができます。  また, http://members10.tsukaeru.net/ariera/soft/wx.html こちらにダウンロード用のテキストファイルも用意してあります。  今度の三連休は久々に出かける予定でいますので,申し訳ありませんが,次回発行日は1週間遅れの11/9(日)とさせていただきたいと思います。 *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- [1] サンプルプログラム 調子笛ソフト改造(1) *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  こちらは,今までの内容が簡単すぎるという方のために,少し複雑なサンプルを作ってみるコーナーです。(初心者の方でも,ソースをコピー&ペーストするだけで,とりあえず動くものは作れます。)  今回から,予告どおり,拙作「電脳調子笛」の改造経緯をご紹介したいと思います。といっても,今回はたぶん現在のプログラムの簡単な解説だけで終わってしまうと思いますが……  ボタンを押すと指定した周波数の音が鳴るだけというつまらないプログラムですが,この後はもう少し使えるプログラムを考えますので,我慢しておつきあいくださいませ。  プログラムの概要とソースはこちらにあります。  http://members10.tsukaeru.net/ariera/soft/fue.html  なお,このプログラムは音を鳴らすのにmmsystemを使っていますのでWindowsでしか動きません。wxでは,音を鳴らす部分までは共通化してくれませんので……  画面とサウンド部分はできるだけ切り分けるようにしていますので,このメルマガでは主に画面部分だけの解説になると思います。 ------------------------------------------------------------------------ (1)現在のソース構成  名前の付け方が安直なので,見れば大体分かると思いますが,念のためソース構成の説明をして起きます。 <ヘッダファイル> fue_gui.h 画面(GUI)部分のヘッダです。wxを使っています。 fue_freq.h 周波数などを設定するヘッダです。改造のときにいじります。 fue_sound.h サウンド関連のヘッダです。Windows専用です。 wxとは関係ないので,今回は解説しませんし,いじりません。 <ソースファイル> fue_gui.cpp 画面(GUI)部分のソースです。wxを使っています。 fue_sound.cpp サウンド関連のソースです。Windows専用です。 wxとは関係ないので,今回は解説しませんし,いじりません。 <その他> fue.rc リソースファイルです。アイコンと一部のイベントIDを設定。 mondrian.ico アイコンファイルです。exeファイルに関連づけています。 makefile.g95 Mingw用のMakefile(手抜き) command.bat コンパイル用のバッチファイル ------------------------------------------------------------------------ (2)クラス構成  クラス構成も簡単です。  音を鳴らす部分はFueSoundというクラスにしてありますが,中身はstatic関数だけなので,インスタンスを作ることはありません。  GUI部分は,HelloWorldと同じく,アプリケーションクラスひとつ(FueApp)と,フレームクラスひとつ(FueFrame)だけからなっています。  フレームの上には,各種ボタンとラベル,条件指定用のラジオボタン1つと,周波数を指定するテキストボックス1つを配置してあります。 ------------------------------------------------------------------------ (3)fue_gui.h の解説  プログラムをコンパイルして実行するか,上のソース置き場のスクリーンショットを見ていただくと,画面上段には「壱越」〜「上無」の音名を表す12個のボタンと「↑」「↓」ボタン,それから各ボタンに対してラベルが二つずつあります。「壱越」とか「口」とかは雅楽用語なのでたいていの方には意味不明かと思いますが,あまり気にしないでください。  「壱越」というのが西洋音楽でいう「レ」=「D」の音で,次の「断金」が「レ#」=「D#」,最後の「上無」が「ド#」=「C#」に相当するので,関数名にはアルファベットのD〜Cp(p は#の意味)を使っているところもあります。  下の段には発音条件のラジオボタンと,周波数を入れるテキストボックス,「周波数を指定して発音」ボタンと「終了」ボタンがあります。それからもう一つ,テキストボックスの隣の「(Hz)」という文字を書くためのラベルがあります。  まず,fue_gui.h の中身をご覧ください。  アプリケーションクラス FueApp は見たまんまなので,フレームクラス FueFrame から見ていきます。  メンバー関数には,コンストラクタ,デストラクタの他,終了時に呼び出すOnClose()と,サイズ変更時に呼び出すOnSize(),各ボタンに関連づけたOn***Button(),ラジオボタンに関連づけたOnPlayConditionRadio() があります。  protectedの DECLARE_EVENT_TABLE() は,以前に説明したように,イベントと関数を関連づけるイベントテーブルを宣言する関数です。  クラス定義の一番下に,privateでpushSoundButton(),disableButtons(), enableButtons()の三つの関数があります。pushSoundButton()は,引数で与えた高さの音を,設定された条件に従って鳴らす関数で,ボタンを押したときに呼ばれるOn***Button()関数の中から呼ばれます。  disableButtons()は,引数で指定した音に対応するボタンを除く全ボタンを押せなくする関数,enableButtons()は全ボタンを押せるようにする関数です。  メンバー変数は,ボタンなどの画面上の部品の他に,設定された条件を保持する変数をいくつか用意しています。  ラベルの中の,soundHoleText は,画面上の「口」とか「〒」とか書いてある部分で,雅楽の龍笛の穴の名前を示しています。そして,soundFreqText の方が,「286.7」とか「302.0」とかいう部分で,これが周波数を表しています。  そして,fue_gui.h の一番下の enum で,各部品のオブジェクトIDを指定しています。これは部品のインスタンスを作るときと,イベントテーブルで関数と関連づけるときに使います。 ------------------------------------------------------------------------ (4)fue_gui.cpp の解説  次に,fue_gui.cpp の中身をご覧ください。こちらが,fue_gui.h で定義したクラスの実装ファイルになります。  最初のIMPLEMENT_APP()とEVENT_TABLEの設定は見たまんまです。  このテーブルの設定や,下のボタンの設定のところは,現状では各ボタンの設定をそのまま書き並べていますが,各音に対応する12個のボタンに対する処理はとてもよく似ているので,マクロやループなどを使って,1回書けば済むようにならないものかと考えています。  FueApp::OnInit()も見たまんま,HelloWorldなどと比べて特に変わったこともないと思います。  次の FueFrame::FueFrame で,フレームの設定をして各部品の初期化をしています。  まず,背景色,ボタンとラベルのフォント,アイコンを設定してから,ボタンを配置していきます。  ボタンを格納する変数は,ヘッダで wxButton* soundButton[FREQ_SET+1]; と宣言されています。配列の添え字には,fue_freq.h で定義したenum SoundName を使います。 ボタンの初期化は,たとえば以下のようにして行っています。 soundButton[ICHIKOTSU] = new wxButton(this, ID_SOUND_D_BUTTON, _T("壱\n越"), wxPoint(20,20), wxSize(30,80)); 一つ目の引数thisは親オブジェクト,二つ目の引数はオブジェクトID,三つ目はボタンのラベル,四つ目は位置,五つ目はサイズです。ラベルは縦書きにしたいので,二文字の間に改行を挟んでいます。  その他の部品の初期化も基本的にはすべて同じです。  他と少し違うのが,146行めあたりの char tmp[32]; というところからです。ここは,発音条件を設定するラジオボタンのところですが,このプログラムでは,「壱越」などの音名ボタンを押したときに,一定時間音を鳴らして止めるか,もう一度同じボタンをクリックするまで鳴らし続けるかを選択できるようにしてあります。  そして,一定時間鳴らす場合の長さは,fue_freq.h のinterval という変数で設定するようになっています。 char tmp[32]; sprintf(tmp, "1回クリックで%d秒発音", interval); これは,たとえばintervalが3だったら,tmpに「1回クリックで3秒発音」という文字列をコピーします。  そして,次の wxString choice[2] = { wxString(tmp), wxString("1回クリックで発音,再クリックで停止") }; では,wxString型の,要素数2の配列を作っています。ラジオボタンでは,選択肢の文字列を配列で設定するため,このようなものを作ります。 playConditionRadio = new wxRadioBox(this, ID_PLAY_CONDITION_RADIO, _T("発音条件"), wxPoint(20,160), wxSize(250,60), 2, choice, 0, wxRA_SPECIFY_ROWS); ラジオボタンの初期化では,三つ目の引数がラジオボタンのタイトル(=何を選択するのか)で,六つ目の引数が選択肢の数,七つ目が選択肢のラベルに入れる文字列の配列,八つ目はラジオボタンを縦と横の両方に並べる場合の縦または横の最大数,九つ目がラジオボタンのスタイル(縦に並べるか横に並べるか)をあらわします。その他の引数は,他の部品と同じです。  次のデストラクタでは,deleteをすべてコメントアウトしてあります。これは,トップウインドウ(フレーム)内で作られたオブジェクトは,アプリケーションが終了するときに自動的にdeleteされるためです。  次の FueFrame::OnSoundDButton()からFueFrame::OnSoundCpButton() までの12個の関数は,音名ボタンを押したときの処理です。ここでは,音名(enum)を引数に入れて,pushSoundButton()を呼び出しているだけです。  そして,次のFueFrame::OnOctUpButton()は「↑」ボタンを押したときの処理です。「↑」ボタンは,鳴らす音を1オクターブ上げる設定をするボタンです。  最初に「↑」ボタンを無効にして「↓」ボタンを有効にしています。このアプリケーションのメインターゲットである龍笛の音域がだいたい2オクターブなので,それ以上の音は設定できないようにするためです。  そして,この後はコメントが間違っていますが,for(int i=ICHIKOTSU; i<=KAMIMU; i++) のループで周波数を設定しています。周波数は fue_freq.h のfreqs 変数から取得しています。  コメントで「周波数ラベルの書き換え」と書いてあるところが,穴名ラベルの書き換えです。竜笛の楽譜では,1オクターブ高い音を出すときは左に朱い点をつけるので,朱色にはしていませんが,穴名に点をつけています。  最後に,オクターブかどうかを示すフラグをTRUEに設定しています。このフラグはFueFrameクラスのメンバー変数です。  次の FueFrame::OnOctDownButton() は「↓」ボタンを押したときの処理で,「↑」ボタンを押したときの処理とほとんど同じです。  次の OnFreqPlayButton() は「周波数を指定して発音」ボタンを押したときに呼ばれる関数ですが,中ではpushSoundButton()を呼び出しているだけです。  「終了」ボタンを押したときのOnCloseButton() は見たまんま,ラジオボタンで発音条件を選択した時のOnPlayConditionRadio() は選択された値を変数に格納しているだけです。ちなみに,選択された時の値は,ラベルの配列の添え字と同じなので,ここでは0か1となります。  次のOnSize()では,何もしていません。wxFrameクラスでは,デフォルトでOnSize()関数が定義されていて,ウインドウの端をつまんでドラッグしたりしてサイズ変更できるようになっていますが,このアプリケーションではサイズを変えられたくないので,OnSize()を上書きして何もしないようにしています。  終了時のOnClose()は見たまんまです。  次のpushSoundButton()が,音名ボタンや「周波数を指定して発音」ボタンを押した時に最終的に呼ばれる関数です。  この関数では,まず最初に,引数から発音する周波数を取得します。この周波数は fue_freq.h のfreqs 変数に入っているものです。  「周波数を指定して発音」のときは,画面上のテキストボックスから値を取得しますが,それが const char* str = freqPlayFreqText->GetValue().c_str(); freq = atof(str); この部分です。GetValue()の戻り値はwxString型なので,c.str()を呼び出してchar*に変換します。そして,atofで文字列から数値に変換しています。  (ラジオボタンの値などは,選択されたときに変数に格納せず,ここでテキストボックスと同じようにして,値を取得するという方法もあります。)  そして,パラメーターをチェックして,異常なときはメッセージを出して音を出さずに抜けます。  音を鳴らす部分は,1回押したら一定時間鳴らす場合と,もう一度押すまで鳴らし続ける場合とで処理が異なります。  1回押したら一定時間鳴らす場合は,もし音が鳴っていたら止めてすべてのボタンを有効にします。しかし,次で見るように,プログラムにバグがなければ,ここを通ることはまずないと思います。  音がなっていなければ,すべてのボタンを無効にして音を鳴らし,指定秒だけ待って音を止め,最後にすべてのボタンを有効にします。うちのPCが非力なせいか,音を鳴らす部分のバグか,設定した時間より長めに音がなるようなので設定時間の80%ぐらいの間だけ鳴らすようにしています。  1回押したらもう一度押すまで鳴らし続ける場合も,音がなっていた場合の処理は同じです。  音がなっていなかった場合は,押したボタンを除くすべてのボタンを向こうにし,音を鳴らします。  FueSound::playFrequency()は,一度呼んだらFueSound::stopPlaying()を呼ぶまで,指定した周波数の音(サイン波)を鳴らし続ける関数です。実装はfue_sound.cpp に書いてありますが,wxとは関係ないので今回は解説しません。  disableButtons()は,引数で指定したボタン以外のすべてのボタンを無効にする関数,enableButtons()はすべてのボタンを有効にする関数です。  以上,現在のプログラムを簡単に説明しましたが,意味不明のところや間違いがありましたら,お気軽にご質問ください。  今までと違って,プログラムを書きながらではなく,大昔に作ったものの解説だったので,今までよりも解説しづらかったです。読む方も分かりにくかったのではないかと思うのですが…… ------------------------------------------------------------------------ (5)改造の内容  今回,何を改造したいかというと,ボタン上の音名と周波数を設定で変えられるようにして,雅楽専用から,普通の西洋音楽でも使えるものに変更したいのです。  設定ファイルなどを使わず,実行画面から設定ダイアログを出し,各音名と周波数が変更できるようにすることと,西洋音楽(平均律),雅楽ともう1種類ぐらいのデフォルト設定を用意して,ボタンを押したらデフォルト設定になるようにすることが目標です。  現在は,周波数は fue_freq.h に,音名は fue_gui.cpp に直書きになっているのですが,これを途中で変更できるように変更します。  さらに,ソース中のほとんど繰り返しに近い部分をマクロにするかループにするかして,もう少し美しいプログラムにしたいと思います。  改造の内容は,次回から解説していきたいと思います。(実は,まだ完成していなかったりするのですが……)  余裕がありましたら,先に改造をすすめてみるのも面白いかと思います。そして,次回私の改造内容があまりに馬鹿馬鹿しかったら,ご一報いただけるとありがたいです。 *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- [2] ワンポイント解説 ダイアログエディタを使う(2) *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  こちらは,初心者向けのワンポイント解説コーナーです。  今回は,前回までの内容を前提としていますので,初購読の方はお手数ですが,バックナンバーを参考にしてください。 ------------------------------------------------------------------------ (1)ダイアログエディタで作った画面を出すプログラム  まず,前回作った「***.wxr」と「***.h」というファイルを探し出してください。  今回はこれを表示するプログラムを書いてみます。説明のため,ファイル名を仮に「test.wxr」「test.h」とします。  そして,同じディレクトリに test.cpp というファイルを作成し,以下の内容を貼り付けてください。 // ここから #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #include "wx/resource.h" #include "test.h" #include "test.wxr" class TestApp : public wxApp { public: virtual bool OnInit(); }; class TestFrame : public wxFrame { public: TestFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style = wxDEFAULT_FRAME_STYLE); void OnClose(wxCommandEvent &event); }; IMPLEMENT_APP(TestApp) bool TestApp::OnInit() { wxResourceParseData(dialog1); TestFrame* frame = new TestFrame(_T("てすと"), wxPoint(50, 50), wxSize(500, 150)); frame->Show(TRUE); SetTopWindow(frame); return TRUE; } TestFrame::TestFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(NULL, -1, title, pos, size, style) { wxDialog *dialog = new wxDialog; if (dialog->LoadFromResource(this, "dialog1")) { dialog->ShowModal(); } dialog->Destroy(); } void TestFrame::OnClose(wxCommandEvent &event) { } // ここまで  これで,一度コンパイルしてみてください。Windowsの方は,リソースファイルも必要ですので, #include "wx/msw/wx.rc" の1行だけ書いた test.rc というファイルも用意してください。  ここで,コンパイルできない方が多いのではないかと思います。  まず,#include "test.wxr" のところで,wxr ファイルをインクルードできないとおこられた方は,test.wxr のファイル名を test_wxr.h などのように変更して,#include "test_wxr.h" としてみてください。  それでもコンパイルできない場合は,wxResourceParseDataが定義されていないとか,LoadFromResourceが定義されていないとかいっておこられているのではないかと思います。  wxrのりソースシステムを使うには,wxUSE_WX_RESOURCES というマクロが1になっていないといけないのですが,私が試したWindows版2.4.0ではデフォルトで0になっていました。  ここでおこられた方は,wxWindowsをインストールしたディレクトリの下のinclude/wx/setup_redirect.h またはsetup.h というファイルを開いて,そこに書いてあるファイルを探してください。Windowsだと include/wx/msw/setup.h だと思います。  このファイルの862行目あたり(2.4.0の場合)に, #define wxUSE_WX_RESOURCES 0 という行があるので,この0を1に書き換えてください。さらに,コメントを読むとPrologIO(最近はwxExprと改名したらしい)も必要らしいので,そのちょっと上の #define wxUSE_PROLOGIO 0 の0も1に書き換えてください。  同じファイルで,「wxUSE_WX_RESOURCES」で検索をかけると,下の方にもう1箇所あります。ここのコメントを読むと,BC++/Win16ではリソースは使えないというようなことが書いてあります。BorlandC++やWin3.1をお使いの方は,リソースは使えないのかもしれません。  ここで,面倒ですが,wxWindowsのライブラリをコンパイルしなおす必要があります。  ライブラリができたら,先ほどのtest.cpp をもう一度コンパイルしてみてください。  これで,私の環境ではうまくいったのですが,もしうまくいかないという方がいらっしゃいましたら,ご一報いただけると嬉しいです。  うまく動けば,空っぽのウインドウの上に,前回作った画面が表示されると思います。まだ,ボタンを押しても何も起きませんし,閉じるボタンもありません。仕方がないのでOSの「×」ボタンで前回作った画面(ダイアログ)とアプリケーションを閉じてください。  長くなりましたので,今回はここまでにします。次回は,上のプログラムの解説をしつつ,少しずつボタンなどが動作するようにしていきたいと思います。 ------------------------------------------------------------------------ 編集後記:  最近,腰痛に悩んでいます。膏薬から磁気治療器,マッサージや整体も試してみたのですが,なかなかすぐに効くものはないようです。試した瞬間は効いた気になっても,一晩眠ると元に戻ってしまいます。  昔は,肩こりはあったものの,腰は酷使しても痛めたことがなく,腰痛持ちを年寄りくさいとバカにしていたので,自分が腰痛持ちになったのはかなりショックでした。他人のトシをバカにすると,やがて自分にもはね返ってくるんですね〜  マッサージとか整体にお金を使うよりも,何か運動したほうがいいのかなぁと思いつつ,何にしようか悩んでしまってなかなか結論が出せません。  次回発行日は2003.11.09(日)の予定です。 ★☆=====================================================================☆★ メールマガジン:wxWindowsでGUIプログラミング 第7号 2003.10.19(Sun) 発行人:二条 ご意見・ご感想はこちらへ: ariera@members10.tsukaeru.net こちらからもメールが送れます http://members10.tsukaeru.net/ariera/other/form/form.cgi 解除・バックナンバーはこちらからどうぞ http://members10.tsukaeru.net/ariera/soft/wx.html マイサイト「気まぐれ歴史散歩」もよろしくお願いします http://members10.tsukaeru.net/ このメールマガジンは「まぐまぐ」を使って配信しています http://www.mag2.com/ ★☆=====================================================================☆★