« シーンをいじるツール | メイン | Direct3Dとくっつける »

2005年11月23日

Teaspire 開発日誌:: Widgetへのアクセスをお手軽に

    

追記: コメントにも書きましたが、付属のwxrcを使って、
wxrc --extra-cpp-code --cpp-code frame.xrc
と言うようにするとヘッダーとソースコードが吐き出されます。
で、出力されたヘッダーに書かれているクラスを継承したものを使えばお手軽にアクセスできます。

このツールは最初に使ってみたものの、使い方が悪くバイナリが出力されて悩んでました。
で、以下の内容は無駄です。


XRCでリソースとソースコードが分離されて喜んでいたのだが、Widgetへのアクセスが面倒臭い。
wxWindow::FindWindowByName(wxT("WidgetName"), this);
ってな感じでWidgetを見付けてきて、それをキャストしてから使う。
リソースを変更して、WidgetNameがなくなったらFindWindowByNameではNULLが返ってくるので、それに対してメソッドを呼び出していたりしたらNULLアクセス例外などになってしまう。

記述の煩わしさに対してはXRCCTRLと言うマクロが準備されていて
XRCCTRL(dlg, "my_textctrl", wxTextCtrl)->SetValue(wxT("default value"));
とすれば、少し楽にアクセスできるようになっている。
が、wxTextCtrlなどとクラス名を指定したり、文字列を渡したりするのがどうも納得いかない。
XRCの方で名前もクラス名も指定済みなのに、また指定するのはいかがなものか。
また、文字列が散らばるのが嫌で、defineか、constメンバにした場合も、再度名前を定義しないといけない。
この辺りをどうにか解消できないかと考える。

求めているのはC++Builderのような使い勝手だ。
Widgetをフォームに置いたらメンバとして追加され、後はそのメンバにアクセスしてプロパティを設定したり、メソッドを読んだり出来る。
それぐらいのお手軽さが欲しい。

型名の問題も何とかしたいので、ソースコードとして何かを追加しなければならなそうだ。
メンバのようにアクセスしたいので、メンバに何かを追加するひつようもあるだろう。
で、そのメンバはポインタみたいなもの。
と言うことは、アロー演算子とキャスト演算子をオーバーロードし、型を扱うのでテンプレートを使い、Widgetを探すのに必要なIDを持つクラスがあれば事足りそうだ。
つまり、次のようなクラスがあればいい。

template <class T>
class GetWidgetPointer
{
    wxWindow *m_Owner;
    long m_ID;

    GetWidgetPointer();
    T *GetWidget() const
    {
        return static_cast<T*>( wxWindow::FindWindowById( m_ID, m_Owner ) );
    }
public:
    GetWidgetPointer( wxWindow *owner, long id ) : m_Owner(owner), m_ID(id) {}
    T *operator->() const { return GetWidget(); }
    operator T*() const { return GetWidget(); }
    long ID() { return m_ID; }
};

これを直接メンバに持ってもいいのだが、そうなるとまた名前を書くはめになる。
XRCからソースコードを作るツールを作った方が良さそうだ。
このツールが出力するソースコードは次のようなものになる。

class MyWidgets
{
    MyWidgets();
public:
    MyWidgets( wxWindow *owner ) :
        MyButton(owner,XRCID("MyButton")),
    {}
public:
    GetWidgetPointer<wxButton>    MyButton;
};

そして、このクラスを使う時は次のようにする。

#include "wx/frame.h"
#include "wx/wx.h"
#include "MyWidgets.h"
class MyFrame : public wxFrame
{
    MyWidgets    Widgets;
public:
    MyFrame( wxWindow* parent=(wxWindow *)NULL);
private:
    DECLARE_EVENT_TABLE()
};

// コンストラクタでオーナーのポインタを入れて初期化
MyFrame::MyFrame( wxWindow* parent)
: wxFrame(parent), Widgets(this)
{
// リソースファイルからwxFrameを作る処理
}

// 後は好きなところで
Widgets.MyButton->SetLabel(wxT("ボタン"));

上の例で、MyButtonはXRC内のボタンの名前。
型はXRC内で指定されている型になる。
Widgets.の部分がC++Builderにくらべて余計だが、まあ我慢できる範囲だろう。

XRCからこのようなソースコードを生成してくれるツールをここに置いた。
出力するクラス名は固定で、1つのリソースにしか対応していない。
メニューについても似たようなものが欲しくなると思うので、複数のフレームやダイアログに対応したものは、必要になった時に作ろうと思う。

実際にこのツールを使う場合は、出力されるソースコードに対してVCならカスタムビルドの設定を、makeを使うのなら依存関係をXRCに設定した方が良い。
そうすれば、XRCファイルを変更した時に自動的に再生成されるようになる。
Widgetの名前を変更したり、削除した場合に、そのWidgetへアクセスしているコードがあれば、コンパイルエラーになる。
なかなかいい感じではないだろうか。



投稿者 Takenori : 2005年11月23日 00:12



コメント

wxrc の --extra-cpp-code でこんな感じのソースを
生成してくれます


class wxPreviewFrame_Base : public wxFrame {
protected:
wxToolBar* ToolBar;
wxChoice* MagnifyChoice;

private:
void InitWidgetsFromXRC(){
wxXmlResource::Get()->LoadObject(this,NULL,_T("wxPreviewFrame_Base"), _T("wxFrame"));
ToolBar = XRCCTRL(*this,"ToolBar",wxToolBar);
MagnifyChoice = XRCCTRL(*this,"MagnifyChoice",wxChoice);
}
public:
wxPreviewFrame_Base::wxPreviewFrame_Base(){
InitWidgetsFromXRC();
}
};

投稿者 W.Dee : 2005年11月23日 23:07

wxrcについては以前、W.Deeさんの日記を読んでいたので、試してみたのですが、なぜかバイナリのようなものが吐き出されました。
コマンドラインでは次のようにしました。
wxrc --extra-cpp-code --output=frame.h frame.xrc
先ほど、もう一度試したのですが、またバイナリのようなものが出てきました。


wxrc --extra-cpp-code --cpp-code frame.xrc
と入力するのですね。
出ました。出ました。
こいつを継承して使うのですね。
なるほど。
ありがとうございます。

投稿者 Takenori : 2005年11月24日 00:40


comments powered by Disqus
Total : Today : Yesterday : なかのひと