鼠标交互的设计模式

时间:2012-04-12 16:56:39

标签: qt design-patterns

我需要对普通鼠标的“理想”设计模式有一些看法 相互作用。

这里简化了问题。我有一个小的3D程序(QT和openGL)和 我用鼠标进行交互。每次互动通常不仅仅是一次 单个函数调用,主要由最多3个函数调用(启动,执行,完成)执行。 例如,相机旋转:这里初始函数调用将提供当前第一个鼠标位置, 而执行函数调用将更新相机等。

然而,对于只有几个交互,硬编码这些(在MousePressEvent,MouseReleaseEvent MouseMoveEvent或MouseWheelEvent等内) 并不是什么大问题,但如果我考虑更高级的程序(例如20次或更多次交互),则需要进行适当的设计。

因此,您如何在QT中设计这样的互动。

我希望我的问题足够清楚,否则不要再抱怨了: - )

由于

3 个答案:

答案 0 :(得分: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,因为它基本上就是构造函数。

这当然是一个相当具体的例子,可能并不完全是你想要的,但它让你了解如何使用多态来清理你的鼠标处理。

答案 1 :(得分:1)

Qt让这很简单。

而不是你曾经写过的所有switch mouse_mode:内容,只需让每个鼠标事件处理函数发出一个信号,即。 mouseDown / mouseUp / mousePosition并使用信号/槽将它们路由到适当的模型函数。

然后,您可以通过连接/断开鼠标中发送的信号来连接/断开鼠标的不同用途(选择,旋转,编辑等)...事件()

答案 2 :(得分:1)

我发现Apple的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);
}

确保在事件发生时调用上述方法,并在需要时调用目标。

这就是目标对我的看法:

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有一个委托对象,用于获取有关其他手势识别器的信息,例如,如果可以同时检测到两个手势,或者一旦检测到另一个手势就必须失败,等等。

某些手势识别器需要比其他手势更多的覆盖,但这个大的PRO是他们的输出是相同的:一个处理方法,通知当前状态(和其他信息)。

我认为值得一看

希望有所帮助:)