|
本サイトは移転しました。旧アドレスからのリダイレクトは2025年03月31日(月)まで有効です。
|
🛈 | ✖ |
統合開発環境Code::Blocksに付属するRADツールwxSmithプラグインを説明する。
wxSmithはCode::Blocksのプラグイン(拡張機能)の一つでwxWidgetsライブラリ使用アプリケーションのGUI開発を支援するRAD(Rapid Application Development)ツールである。wxSmithはトップレベルウィンドウ(wxFrame、wxDialog)あるいはパネル(wxPanel)の派生であるユーザー定義ウィンドウクラスを記述するインクルードファイル(*.h)/ソースコードファイル(*.cpp)をwxSmithリソースファイル(*.wxs)と共に生成し、wxSmithエディタでGUIをWYSIWYG(What You See Is What You Get)編集する。編集は*.h/*.cpp/*.wxsに自動反映される。wxSmithエディタの操作性は一般的なグラフィック編集ソフトに程遠く使いこなしは習熟を必要とする。
wxSmithプラグインはCode::Blocksにシームレスに統合されて、[wxSmith|Add wxXXX]を実行してトップレベルウィンドウあるいはパネルを生成する。ダイアログなら[wxSmith|Add wxDialog]を実行して[New wxDialog resource]ダイアログを開きクラス名、ファイル名を入力して[OK]を押す。[Multiple selection]ダイアログが開きダイアログを使用するターゲットにチェックして[OK]を押せば*.h/*.cpp/*.wxsにwxDialog派生をユーザー定義ウィンドウとして生成してwxSmithエディタが開く。[New wxDialog resource]ダイアログで[OK]の前に[Advanced options]をクリックすれば詳細なオプション設定できるがデフォルトのままで良い。
ウィザード(K1、K2など)の作成するwxWidgetsプロジェクトはメインウィンドウの*.wxsを自動作成する。こういった既成の*.wxsをwxSmithエディタに読み込むには[Management]ウィンドウ[Projects]ページツリービューで[...|Resources|wxsmith|*.wxs]をダブルクリックする。[Project|Add files]または[Project|Remove files]で*.wxsを*.h/*.cppと同様にプロジェクトへ追加またはプロジェクトから削除できるが、これらをwxSmithは認識しない。*.wxsの追加/削除をwxSmithに認識させるにはXML形式で記述されるプロジェクトファイル(*.cbp)を直接編集し[...|Extensions|wxsmith|resources]ノードに追加/削除する。
信頼できるwxSmithドキュメントはチュートリアルだけでCode::Blocksに統合されたユーザーインターフェースを説明する用語が明確に定義されていないため、本サイトは以下に定義する。wxSmithのユーザーインターフェースは[Management]ウィンドウ[Resources]ページに表示されるwxSmithリソースマネージャと、コードエディタの代わりに表示されるwxSmithエディタである。
wxSmithリソースマネージャはワークスペースに読み込まれている全プロジェクトのwxSmithリソースをツリービューで管理する。[Resources]はルートノードで下位にプロジェクトノードを持つ。その下位はユーザー定義ウィンドウを基底クラス(wxFrame、wxDialog、wxPanel)で分類するノードで、それぞれの下位にユーザー定義ウィンドウノードを持つ。ユーザー定義ウィンドウノードは基底クラス名のノードと[Tools]ノードを持ち、前者はウィンドウ配置されるコンポーネント(ウィジェットコンポーネント)のルートであり、後者はウィンドウ配置されないコンポーネント(ツールコンポーネント)のルートであり、いずれも下位に"コンポーネントノード"を所有関係に基づき階層表示する。前者は基底クラスから継承するプロパティ/イベントを表示編集するノードを兼ね、これを"ウィンドウノード"と呼ぶ。ツリービューの下にプロパティエディタとイベントエディタがあり、両者をアイコンボタンで切り替えて選択したウィンドウ/コンポーネントのプロパティ/イベントを表示編集する。ウィンドウ/コンポーネント以外のノードはプロパティ/イベントを持たないため、それらを選択するとプロパティ/イベントエディタは選択前の状態を維持する。
wxSmithエディタは中央にWYSIWYG表示されるユーザー定義ウィンドウにウィジェットコンポーネントを配置する。配置するコンポーネントは下のコンポーネントパレットから選択する。ウィンドウに配置できないツールコンポーネントは上のツールホルダーパネルに配置する。配置したコンポーネントの選択はリソースマネージャとエディタで連動するが、リソースマネージャでウィンドウ/コンポーネント以外のノードを選択する場合のみエディタは選択前の状態を維持する。右にコマンドパネルがあり、配置方法の選択、配置されたコンポーネントの削除、プレビューなどを行う。
"コンポーネント"はチュートリアルに準拠して定義する。wxSmithのリソース(ユーザー定義ウィンドウ)はコンポーネントで構成される。全てのボタン、テキストボックス、リストなどはコンポーネントである。サイザー、スペーサー(空スペースを加えるもの)、メニュー項目、タイマーといった抽象的な存在もコンポーネントである。すなわち基本的にリソースに含まれるもの全てをコンポーネントと呼ぶ。
コンポーネントはウィンドウに配置される"ウィジェットコンポーネント"とウィンドウに配置されない"ツールコンポーネント"に二分され、多数派はウィジェットコンポーネントである。ウィジェットコンポーネントで自らに他のウィジェットコンポーネントを部品として配置できるもの(例えばwxPanel)を"コンテナコンポーネント"と呼ぶ。トップレベルウィンドウは便宜上コンテナコンポーネントに含める。ウィジェットコンポーネントのほとんどはwxWidgetsコントロール(wxControl派生)に対応するが、コンポーネントパレット[Layout]ページのサイザーはwxWidgetsサイザー(wxSizer派生)に対応し、スペーサー(Spacer)のようにそもそもwxWidgetsオブジェクトに対応しないものもある。ツールコンポーネントは[Dialogs]ページのダイアログと[Tools]ページのその他ツールであり、ダイアログはwxWidgetsダイアログ(wxDialog派生)に対応し、その他ツールはそれぞれ何らかのwxWidgetsオブジェクトに対応する。その他ツールに含まれるメニューバー(wxMenuBar)、ステータスバー(wxStatusBar)、ツールバー(wxToolBar)はウィンドウの一部として表示されるため本来はウィジェットコンポーネントとするべきと思うが、wxSmithはこれらをツールコンポーネントとして扱いWYSIWYGに配置せず、かつwxFrame派生のユーザー定義ウィンドウのみに追加できる。wxWidgetsライブラリはwxStatusBarとwxToolBarをwxControl派生としてこの制約は課しておらず、マニュアル実装でwxWindow派生に普通に追加できる。一方wxMenuBarはwxControl派生でなくこのような追加はできない。
wxSmithはwxWidgetsライブラリをより高次で再抽象化し、wxSmithコンポーネントはwxWidgetsオブジェクトにそのまま対応しない。wxSmithコンポーネントプロパティのあるものは対応するwxWidgetsオブジェクトのコンストラクタへの実引数であり、あるものはセッターメンバ関数への実引数であり、あるものは別オブジェクトのメンバ関数に渡す実引数であり、その他様々である。重要な例はほとんどのコンポーネント(例えばwxTextCtrl)が持つIs memberプロパティで、これをチェックするとwxTextCtrlインスタンスをメンバ変数に保持してコンストラクタ外からも参照できる。別の重要な例はサイザー(例えばwxBoxSizer)に配置したウィジェットコンポーネント(例えばwxTextCtrl)で、wxBoxSizerがwxTextCtrlを配置するためのサイザーパラメータがwxTextCtrlのプロパティとして表示される。サイザーパラメータはソースコードでwxBoxSizerインスタンスメンバ関数への実引数であり、サイザー配置しない場合はプロパティとして表示されない。Is memberもサイザーパラメータもC++クラス的にはwxTextCtrlのプロパティと呼べず、このようにコンポーネントプロパティとソースコードの関係は複雑で直感から程遠い。
wxSmithが生成挿入するコードはマーカーとなるコメント行//(*<種類>(<クラス名>)と//*)に挟んで明示する。他にマーカー行で明示しないがソースコードにイベントハンドラメンバ関数定義のスケルトンを挿入する。マーカー行に挟まれた部分はマニュアル編集を想定せず、編集したとしてもwxSmithの編集が常に上書きする。Handlersは例外で、wxSmithイベントエディタからハンドラを除外してもHandlersからメンバ関数宣言は削除されない。ハンドラメンバ関数を削除するにはソースコードのメンバ関数定義と一緒にマニュアル削除する。Handlers、Identifies、Declarationsはクラススコープに挿入されてアクセス指定子(private/protected/public)は[New wxXXX resource]ダイアログ[Advanced options|Scopes]の設定に従う。なおアクセス指定子はマーカー行に挟まれておらず、マニュアル編集で変更できる。
ファイル | 種類 | 説明 | 備考 |
---|---|---|---|
インクルード | Headers | 公開されるwxWidgetsインクルードファイル | - |
Handlers | イベントハンドラメンバ関数の宣言 | - | |
Identifies | wxWindow派生コンポーネントのID値宣言 | - | |
Declarations | メンバとして保持されるwxWindow派生コンポーネントの宣言 | - | |
ソースコード | InternalHeaders | 公開されないwxWidgetsインクルードファイル | - |
IdInit | wxWindow派生コンポーネントのID値定義 | - | |
EventTable | 静的なイベントマップ | 使用しない | |
Initialize | ウィンドウとコントロールの構築、動的なイベントマップ | - | |
Destroy | ウィンドウとコントロールの解体 | 使用しない |
動作確認(1)のDesktop1プロジェクトメインウインドウで生成挿入部を例示する。
コンポーネントをWYSIWYG表示されるユーザー定義ウィンドウあるいはツールホルダーパネルに配置し、プロパティ/イベントをそれぞれのエディタで編集する。コンポーネントの配置方法は変則的でグラフィック編集ソフト的なものを期待すると面食らう。コマンドパネルボタンの機能を説明する。
コマンド | 機能 |
---|---|
Insert new widgets by pointing with mouse | 新しいコンポーネントをマウスカーソル位置に挿入する |
Insert new widgets into current selection | 新しいコンポーネントを現在の選択の中に挿入する |
Insert new widgets before current selection | 新しいコンポーネントを現在の選択の前に挿入する |
Insert new widgets after current selection | 新しいコンポーネントを現在の選択の後に挿入する |
Delete current selection | 現在の選択を削除する |
Show preview | プレビューを表示する |
Open / Close Quick Properties panel | クイックプロパティパネルを開く/閉じる |
[Insert ...]は挿入モードを選択するもので、選択した挿入モードはアイコンにチェックマークが付く。[Delete ...]と[Show ...]は説明する必要は無いだろう。[Open / Close ...]はサイザープロパティ設定を簡易化するツールを開く/閉じるが、開発者も役に立たないと認めていて無視する。
ツールコンポーネントの配置は単純で、挿入モードに関係なくパレットで選択すれば即時にツールホルダーパネルに配置される。配置されたツールコンポーネントの順番はクリップボード操作([Edit|Cut]と[Edit|Paste])で変更できる。
ウィジェットコンポーネントの配置はサイザーコンポーネントを用いる場合とそうでない場合で操作性が全く異なる。一般的なGUI編集ツール(例えばエクセルのVBAフォームデザイナー)はレイアウトフリー(任意の位置に部品を配置できる)でこれに近いものはサイザーコンポーネントを用いない場合だが、同等の操作性は期待できない。VBAフォームデザイナーならツールボックスからコントロールをドラッグドロップするが、同様を行うには[Insert ... by pointing with mouse]を選択して、パレットでコンポーネントを選択して、配置したい場所をクリックする。つまりパレットから直接ドラッグドロップできない。最初のコンポーネントはなぜかウィンドウサイズに合わせて拡大する。2番目のコンポーネントは最初のコンポーネントがコンテナコンポーネントならその中に配置するが、そうでないと最初のコンポーネントが突然縮小する。配置したコンポーネントはマウスで移動/サイズ変更できるが、いくつかのコンポーネントは不可解な挙動を示す。例えばwxPanelコンポーネントをサイズ拡大すると短距離のドラッグ移動ができなくなり、まるで移動開始の閾値を自サイズ相対値で判定しているようだ。コンポーネントをコンテナコンポーネントへドラッグして挿入する事はできる。クリップボード操作は可能だが切り取ったコンポーネントを空のコンテナコンポーネントに貼り付ける事は恐らくできない。その他うまく説明できない事態に何度も遭遇し、最悪にはCode::Blocksがクラッシュして全ての編集を失う。
これらはフラストレーションを溜め込みwxSmithの有用性を否定しかねない結果をもたらすが、想像するにwxSmithはレイアウトフリーが不得意で、サイザーコンポーネントで必ずレイアウト管理すると決めれば十分使用に耐える。チュートリアルもサイザーコンポーネント使用を前提とする。
サイザーコンポーネント(wxSizer派生)はコンテナコンポーネントが所有し、コンテナに配置されるウィジェットコンポーネント(コンテナ部品)のレイアウトとコンテナコンポーネントのサイズを管理する。最初に"所有"を定義する。wxSmithコンポーネントの"所有"はwxSmithリソースマネージャのツリービューにおける上下関係とする。wxWidgetsオブジェクトの"所有"は所有オブジェクトが解体時に被所有オブジェクトを解体する関係とする。コンテナコンポーネントがサイザーコンポーネントを所有する場合、wxSmithコンポーネントではサイザーコンポーネントがコンテナ部品を所有するが、wxWidgetsオブジェクトではコンテナオブジェクトがコンテナ部品に対応するオブジェクトを所有して、この例外を除けば両者は概ね一致する。以降はwxSmithコンポーネントの文脈を用いる。
サイザーコンポーネントをまとめる。これ以外にサイザーとコントロールと組み合わせたコンポーネントが存在するが省略する。サイザーコンポーネントは他のサイザーコンポーネントをコンテナ部品に所有してさらに複雑なレイアウトに対応する。
サイザー | 説明 | 部品配置 |
---|---|---|
wxBoxSizer | 縦横いずれかで一次元配置する | シーケンシャル |
wxGridSizer | 等幅、等高のグリッドで二次元配置する | シーケンシャル |
wxFlexGridSizer | 可変幅、可変高のグリッドで二次元配置する | シーケンシャル |
wxGridBagSizer | 可変幅、可変高、可変スパンのグリッドで二次元配置する | ランダム |
シーケンシャル部品配置はウィジェットコンポーネントを構築順でグリッド配置する。ランダム部品配置はコンポーネントのプロパティで配置セルを指定する。主にシーケンシャル部品配置で任意セルを空白とする手段としてSpacerコンポーネントが用意される。シーケンシャル部品配置は構築順が配置を決定するが、ランダム部品配置はプロパティが指定する。いずれのサイザーもウィジェットコンポーネントの配置方法は同じで二つある。
第一の方法はコマンドパネル[Insert new widgets by pointing with mouse]を選択する。配置したいコンポーネントをコンポーネントパレットで選択してマウスをWYSIWYG表示ユーザー定義ウィンドウへ移動すればターゲットとなり得るコンポーネントがダークブルーで示される。ターゲットは空のサイザーコンポーネントあるい全コンテナ部品のどちらかで、前者であればそのままクリックして最初のコンポーネント構築となる。後者であればマウスを任意のコンテナ部品へ移動して左半分または右半分がライトブルーに変化し、クリックして前あるいは後に構築を挿入する。後者の場合にコンテナ部品以外をクリックすれば最後に構築を追加する。
第二の方法は最初にターゲットとなるコンポーネントをWYSIWYG表示ユーザー定義ウィンドウあるいはリソースマネージャのツリービューで選択する。コマンドパネル[Insert new widgets into current selection]、[Insert new widgets before current selection]、[Insert new widgets after current selection]のいずれかを選択し、パレットでコンポーネントを選択すればコマンドに従い構築される。コマンドは状況検知して例えばコンテナでないコンポーネントに[Insert ... into ...]を選択できない。コンポーネントが重なりターゲットがマウス指定できない場合は第一の方法は無理で、ツリービュー選択で第二の方法を用いる。
コンテナ部品はドラッグ移動で構築順やコンテナを変更できる。シーケンシャル部品配置なら構築順の変更はそのまま配置の変更となる。マウスでサイズ変更できるが、サイザーコンポーネントとそれがサイズ管理するコンポーネントは変更を拒絶する。クリップボード貼り付けは選択の前に挿入されて概ね直感通りだが、空のコンテナへは貼り付けできない。
サイザーコンポーネントの利用は複雑でトライアンドエラーで経験を積むしかない。サイト作成者はwxBoxSizerとwxFlexGridSizerの組み合わせに限定し、経験上これで十分と考えている。コンポーネントのIs memberプロパティはコンポーネント対応wxWidgetsオブジェクトをコンストラクタの外から参照する場合のみチェックしている。IdentifierプロパティはwxID_ANYとしてwxWidgetオブジェクトコンストラクタに渡しID値の生成を委ねるが、wxSmithはをその入力文字列をウィンドウ名としても渡してしまう。そのため全てが同じウィンドウ名_T("wxID_ANY")を持ち、コントロールをウィンドウ名で検索する事はできない。コンテナ部品はサイザープロパティでサイザー中の振る舞いを定義するが、その最重要はExpandとProportionと考える。トップレベルウィンドウが固定サイズならExpandをTRUE、Proportionを0を基本とする。wxStaticTextは例外で、自身のプロパティで縦方向の文字アライメントを定義できないため、ExpandをFALSEとしてVertical alignをCenterとする。何であれプレビューと修正の繰り返しは必須と覚悟する。
KTxtEditプロジェクトのオプション設定ダイアログの作成をサンプルとして作成する。[wxSmith|Add wxDialog]の[New wxDialog resouce]ダイアログで[Class Name]テキストボックスにOptionDialogを入力し[OK]を押す。wxSmithエディタが生成されたOptionDialog.wxsスケルトンを読み込み開くので、以下のコンポーネントを順次配置する。チュートリアルは最初にwxBoxSizerとwxPanelを配置してwxPanelにコントロールを追加せよとするが、wxDialog派生ではメリットは見当たらず本サイトは省略する。Notebook(wxNotebookコンポーネント)にページを追加するには、選択右クリックで表示されるコンテキストメニュー[Add new page]を実行する。TextCtrlAppearance(TextCtrlコンポーネント)のProportionを1として[Appearance]ページの隙間を埋める。Notebook(wxNotebookコンポーネント)のProportionが0のままではTextCtrlAppearanceが不要に拡大してStdDialogButtonSizer(wxStdDialogButtonSizerコンポーネント)が見えなくなるケースが存在し、これも1とする。StdDialogButtonSizer(wxStdDialogButtonSizerコンポーネント)はExpandをTRUEとするとボタンが右寄せとなるためFALSEとして、Horizontal alignをCenterとする。
コンポーネント | Var name | Is member | Identifier | Expand | Proportion | その他のプロパティ |
---|---|---|---|---|---|---|
wxDialog | - | - | - | - | - | Title="Option settings" |
└ wxBoxSizer | BoxSizer | FALSE | - | - | - | Orientation="wxVERTICAL" |
├ wxNotebook | Notebook | TRUE | wxID_ANY | TRUE | 1 | 右クリック[Add new page]でwxPanelを追加する |
│├ wxPanel | PanelFile | FALSE | wxID_ANY | - | - | Page name="File", Background="Window background" |
││└ wxFlexGridSizer | FlexGridSizerFile | FALSE | - | - | - | Cols="2", Glowable cols="0" |
││ ├ wxStaticText | StaticTextRecentFilesMax | FALSE | wxID_ANY | FALSE | 0 | Label="Recent files max", Horizontal align="Left", Vertical align="Center" |
││ ├ wxSpinCtrl | SpinCtrlRecentFilesMax | TRUE | wxID_ANY | TRUE | 0 | Value="10", Min="1", Max="20" |
││ ├ wxStaticText | StaticTextDefaultEncoding | FALSE | wxID_ANY | FALSE | 0 | Label="Default encoding", Horizontal align="Left", Vertical align="Center" |
││ ├ wxChoice | ChoiceDefaultEncoding | TRUE | wxID_ANY | TRUE | 0 | |
││ ├ Spacer | - | - | - | TRUE | 0 | |
││ ├ wxCheckBox | CheckBoxDefaultByteOrderMark | TRUE | wxID_ANY | TRUE | 0 | Label="Byte order mark" |
││ ├ wxStaticText | StaticTextDefaultEndOfLine | FALSE | wxID_ANY | FALSE | 0 | Label="Default end of line", Horizontal align="Left", Vertical align="Center" |
││ └ wxChoice | ChoiceDefaultEndOfLine | TRUE | wxID_ANY | TRUE | 0 | |
│└ wxPanel | PanelAppearance | FALSE | wxID_ANY | - | - | Page name="Appearance", Background="Window background" |
│ └ wxBoxSizer | BoxSizerAppearance | FALSE | - | - | - | Orientation="wxVERTICAL" |
│ ├ wxTextCtrl | TextCtrlAppearance | TRUE | wxID_ANY | TRUE | 1 | Style="wxTE_MULTILINE, wxTE_READONLY" |
│ └ wxBoxSizer | BoxSizerAppearanceButtons | FALSE | - | TRUE | 0 | Orientation="wxHORIZONTAL" |
│ ├ wxButton | ButtonAppearanceForegroundColor | FALSE | wxID_ANY | TRUE | 0 | Label="FG Color..." |
│ ├ wxButton | ButtonAppearanceBackgroundColor | FALSE | wxID_ANY | TRUE | 0 | Label="BG Color..." |
│ └ wxButton | ButtonAppearanceFont | FALSE | wxID_ANY | TRUE | 0 | Label="Font..." |
└ wxStdDialogButtonSizer | StdDialogButtonSizer | FALSE | - | FALSE | 0 | Horizontal align="Center" |
[File]、[Appearance]の2ページからなるオプション設定ダイアログが生成できるはずだ。