2012-04-12 96 views
3

我需要一些关于什么是一般鼠标 交互的“理想”设计模式的一些意见。鼠标交互的设计模式

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

然而,对于只有一对夫妇的相互作用,这些硬编码(内部MousePressEvent ,MouseReleaseEvent MouseMoveEvent或MouseWheelEvent等) 不是什么大问题,但是如果我考虑一个更高级的程序(例如20或更多的交互),那么就需要一个适当的设计。

因此,您如何设计QT内部的这种交互。

我希望我做我的问题很清楚,否则也懒得抱怨:-)

感谢

回答

4

我建议使用多态和工厂方法模式。这里有一个例子:

在我的Qt程序我有QGraphicsScenes和QGraphicsItems与mousePressEvent,mouseMoveEvent和mouseReleaseEvent,它看起来像这样:

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

感谢安东尼您的详细解答。我将尝试实施工厂方法,并结合信号/插槽机制。否则,我看不到如何将openGL拾取连接到实际对象,而这只能给我一些id。 – mike 2012-04-13 06:22:30

1

的Qt使得这个完美的简单。

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

然后你就可以适应鼠标的不同用途(选择,旋转,编辑等)的连接/断开不同时隙中的鼠标发出的信号......事件()

+0

马丁,感谢您的答复。结合工厂模式,信号/插槽可能是一个整洁的解决方案。 – mike 2012-04-13 06:24:53

1

我发现苹果的UIGestureRecognizer设计相当不错,可扩展。

这个想法是分离手势(或交互)的识别和将被触发的动作。

您需要实现基本或抽象的GestureRecognizer类,该类能够基于事件MousePressEvent,MouseReleaseEvent,MouseMoveEvent或MouseWheelEvent等识别某种交互或手势。手势记录器有一个定期报告变化的目标。

例如你很基本的类将是这样的:(对不起我那可怜的半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是它们的输出是相同的:处理器方法通知有关当前状态(和其他信息)。

我觉得是值得考虑看看它

希望它能帮助:)

+0

你好nacho4d,非常感谢你的详细解答。目前,GestureRecognizer设计似乎是针对我的特定问题的一般方法。不过,我会记住它,以备后用。谢谢 – mike 2012-04-13 06:31:03