3

一般的なマウスの相互作用の「理想的な」デザインパターンについて、いくつか意見が必要です。

ここに単純化された問題があります。私は小さな3Dプログラム(QTとopenGL)を持っており、対話にはマウスを使用しています。すべての対話は通常、単一の関数呼び出しだけでなく、ほとんどの場合、最大3つの関数呼び出し(開始、実行、終了)によって実行されます。たとえば、カメラの回転:ここでは、最初の関数呼び出しは現在の最初のマウス位置を提供しますが、実行中の関数呼び出しはカメラなどを更新します。

ただし、2、3のインタラクションの場合、これら(MousePressEvent、MouseReleaseEvent MouseMoveEvent、MouseWheelEventなどの内部)をハードコーディングすることは大したことではありませんが、より高度なプログラム(20以上のインタラクションなど)について考える場合は、適切な設計が必要です。

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

私は自分の問題を十分に明確にしたことを願っています、そうでなければ文句を言わないでください:-)

ありがとう

4

3 に答える 3

4

ポリモーフィズムとファクトリメソッドパターンを使用することをお勧めします。次に例を示します。

私のQtプログラムには、mousePressEvent、mouseMoveEvent、mouseReleaseEventを含むQGraphicsScenesとQGraphicsItemsがあります。これらは次のようになります。

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は必要ありません。

これはもちろんかなり具体的な例であり、おそらくあなたが望むものとは正確には一致しませんが、ポリモーフィズムを使用してマウスの処理をクリーンアップする方法についてのアイデアを提供します。

于 2012-04-12T18:02:31.790 に答える
1

Qtはこれを美しくシンプルにします。

switch mouse_mode:あなたが書いていたすべてのものの代わりに、単に各マウスイベントハンドラー関数にシグナルを発させるようにします。mouseDown / mouseUp / mousePositionを使用し、信号/スロットを使用してそれらを適切なモデル関数にルーティングします。

次に、Mouse ... Event()で送信される信号にさまざまなスロットを接続/切断することで、マウスのさまざまな使用法(選択、回転、編集など)に対応できます。

于 2012-04-12T17:25:42.600 に答える
1

AppleのUIGestureRecognizerのデザインは非常に素晴らしく、拡張可能だと思います。

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

イベントMousePressEvent、MouseReleaseEvent MouseMoveEvent、MouseWheelEventなどに基づいて特定のインタラクションまたはジェスチャを認識できる基本または抽象GestureRecognizerクラスを実装する必要があります。GestureRecongnizersには、変更を定期的に報告するターゲットがあります。

たとえば、非常に基本的なクラスは次のようになります:(私の貧弱なsemi 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);
}

イベントが発生しているときに上記のメソッドが呼び出され、必要に応じてターゲットを呼び出す必要があることを確認してください。

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

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?
    }
}

UIGestureRecognizerの実装は非常に優れており、同じレコグナイザーと複数のレコグナイザーの複数のターゲット/メソッドを同じビューに登録できます。UIGestureRecognizersには、他のジェスチャ認識機能に関する情報を取得するために使用されるデリゲートオブジェクトがあります。たとえば、2つのジェスチャを同時に検出できる場合や、一方が検出されたらすぐに失敗する必要がある場合などです。

一部のジェスチャレコグナイザは他のジェスチャよりも多くのオーバーライドを必要としますが、これの大きな利点は、出力が同じであるということです。つまり、現在の状態(およびその他の情報)を通知するハンドラメソッドです。

一見の価値があると思います

それが役に立てば幸い :)

于 2012-04-12T18:25:05.573 に答える