2012-04-12 5 views
3

一般的なマウスの「理想的な」デザインパターンについての意見が必要です マウスインタラクションのデザインパターン

ここでは単純化された問題です。私は小さな3Dプログラム(QTとOpenGL)を持っており、 私は相互作用のためにマウスを使用します。すべての相互作用は通常、単一関数呼び出しであるだけでなく、主に3つまでの関数呼び出し(開始、実行、終了)によって実行されます。例えば 、カメラの回転:ここで最初の関数呼び出しは、これらの(MousePressEvent内部にハードコーディング、相互作用の唯一のカップルのため、等

しかしカメラが更新され、実行する関数呼び出しに対し を現在の第1のマウス位置を提供します、MouseReleaseEvent MouseMoveEventまたはMouseWheelEventなど) は大したことではありませんが、より高度なプログラム(20以上の相互作用など)について考えると、適切な設計が必要です。

したがって、このような相互作用をQT内でどのように設計しますか?

私はそう:-)

答えて

4

私はポリモーフィズムを使用することをお勧めし、ファクトリメソッドパターン。ここでは例です:私はこのようになりmousePressEvent、mouseMoveEvent、およびmouseReleaseEventとQGraphicsScenesとQGraphicsItems、持っている私のQtのプログラムで

:この特定のケースでは

void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event) 
{ 
    // call factory method, which returns a subclass depending on where click occurred 
    dragHandler = DragHandler::createDragHandler(event /* and other relevant stuff */); 
} 

void CustomItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 
{ 
    dragHandler->onMouseMove(event); 
} 

void CustomItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 
{ 
    dragHandler->onMouseRelease(event); 
    delete dragHandler; 
} 

アイデアは、私がクリックした場所に応じてということですCustomItemでは、マウスの押下、移動、および解除の機能が異なります。たとえば、アイテムの端をクリックするとドラッグするとサイズが変更されますが、アイテムの中央をクリックするとドラッグするとドラッグされます。 DragHandler :: onMouseMoveとDragHandler :: onMouseReleaseは、マウスがどこで押されたかによって必要な特定の機能を提供するために、サブクラスによって再実装される仮想関数です。基本的にコンストラクタなので、DragHandler :: onMousePressの必要はありません。

もちろんこれはかなり具体的な例であり、おそらくあなたは望んでいない、まさに、それはあなたがあなたのマウス操作をクリーンアップするために多型を使用する方法のアイデアを提供します。

+0

あなたの詳細な答えはアンソニーありがとうございます。私はシグナル/スロット機構と組み合わせたファクトリメソッドを実装しようとします。さもなければ、私は実際のオブジェクトで私にいくつかのIDを与えるだけのOpenGLピッキングをどのように接続できるのか分かりません。 – mike

1

Qtは、これが美しく簡単になり

おかげで文句を言う気にしない、十分に私の問題を明らかにしたいと考えています。

あなたが書いていたすべてのものの代わりに、マウスイベントハンドラの各関数にシグナルを送出させるだけです。 mouseDown/mouseUp/mousePositionを呼び出し、シグナル/スロットを使用して適切なモデル関数にルーティングします。

は、その後、あなたがマウスで送信された信号に接続/切断異なるスロットによるマウスの異なる用途を(選択、回転、編集など)に対応することができます...イベント()

+0

マーティンがお役に立てば幸いです。ファクトリー・パターンと組み合わせて、信号/スロットはきちんとした解決策になります。 – mike

1

私はアップルのUIGestureRecognizerデザインが非常に素晴らしく、拡張可能であることがわかりました。

アイデアは、ジェスチャの認識(または相互作用)とトリガされるアクションを分離することです。

イベントに基づいて特定の相互作用またはジェスチャを認識できる、基本的または抽象的なGestureRecognizerクラスを実装する必要があります。MousePressEvent、MouseReleaseEvent MouseMoveEventまたはMouseWheelEventなどGestureRecongnizersは定期的に変更を報告するという目標を持っています。

たとえば、あなたの非常に基本的なクラスは以下のようになります:(申し訳ありません私の貧しい半C++擬似コード...最近、私はそれを使用していない多くのこと)

class Recognizer { 
int state; // ex: 0:possible, 1:began, 2:changed, 3:ended/recognized 4:cancelled 
protected: 
void setTarget(void &theTarget); // or even better a touple, target/method. In this case target is assumed to have a method gestureHandle(Recognizer *r); 
virtual void mouserPress() = 0; 
virtual void mouserRelease() = 0; 
virtual void mouserMove() = 0; 
virtual void mouserWheel() = 0; 
... 
} 

そして、あなたが検出したい場合メソッドが呼び出され、上記のイベントが起こっているとき、マウスでスワイプ

class SwipeRecognizer : Recognizer { 
int direction; // ex: 0:left2right 1:bottom2top 2:... 
private: 
void mouserPress() { 
    state = 0; // possible. You don't know yet is the mouse is going to swipe, simple click, long press, etc. 
    // save some values so you can calculate the direction of the swipe later 
    target.gestureHandle(this); 
}; 
void mouserMove() { 
    if (state == 0) { 
     state = 1; // it was possible now you know the swipe began! 
     direction = ... // calculate the swipe direction here 
    } else if (state == 1 || state == 2) {// state is began or changed 
     state = 2; // changed ... which means is still mouse dragging 
     // probably you want to make more checks here like you are still swiping in the same direction you started, maybe velocity thresholds, if any of your conditions are not met you should cancel the gesture recognizer by setting its state to 4 
    } 
    target.gestureHandler(this); 
}; 
void mouserRelease() { 
    if (state == 2) { // is swipping 
     state = 3; // swipe ended 
    } else { 
     state = 4; // it was not swiping so simple cancel the tracking 
    } 
    target.gestureHandler(this); 
}; 
void mouserWheel() { 
    // if this method is called then this is definitely not a swipe right? 
    state = 4; // cancelled 
    target.gestureHandler(this); 
} 

念、必要なときに、彼らはターゲットを呼び出す必要があります。

これは、ターゲットが私にどのように見えるかです:UIGestureRecognizerの

class Target { 
... 
void gestureHandler(Recognizer *r) { 
    if (r->state == 2) { 
     // Is swipping: move the opengl camera using some parameter your recognizer class brings 
    } else if (r->state == 3) { 
     // ended: stop moving the opengl camera 
    } else if (r->state == 4) { 
     // Cancelled, maybe restore camera to original position? 
    } 
} 

実装は非常にいいですし、同じビューに同じ認識機能と、いくつかの認識機能のために、いくつかのターゲット/メソッドを登録することができます。

を2つのジェスチャーを同時に検出することが可能であれば UIGestureRecognizersは、例えば、他のジェスチャー認識機能についての情報を取得するために使用されるデリゲートオブジェクトを持っている、または1つはすぐに他が検出されるように失敗しなければならない必要がある、などいくつかのジェスチャーレコグナイザは、他のものよりも多くのオーバーライドを必要としますが、これの大きなPROは、出力が同じであることです:現在の状態(およびその他の情報)を通知するハンドラメソッド。私はそれ

見てみる価値はあると思い

は、それが、返信に感謝を:)

+0

こんにちはnacho4d、あなたの詳細な答えをありがとう。現時点では、GestureRecognizerのデザインは、私の特定の問題にはあまりにも一般的なアプローチであるようです。それにもかかわらず、後の使用のために、私はそれを私の心に残していきます。ありがとう – mike

関連する問題