首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >QStateMachine - QMouseEvent

QStateMachine - QMouseEvent
EN

Stack Overflow用户
提问于 2016-06-06 11:19:05
回答 2查看 401关注 0票数 0

在另一个问题中,您告诉我使用QStateMachine。

我对Qt很陌生,这是我第一次使用对象,所以我犯了很多逻辑上的错误,所以使用QStateMachine是个大问题。

这是唯一的办法?我试着解释一下我的计划:

我想要创建一个卡片的游戏,在以前的版本中,我使用了一个旧的图形库,其中包含以下命令:

代码语言:javascript
复制
-> print cards on the scene 
-> wait for a mouse input (with a do-while)
-> if(isMouseClick(WM_LBUTTONDOWN)) 
-> if(mouse position is on the first card) 
-> select that card. So i wish to do the same thing with QGraphics. 

我用这种方式告诉节目:

代码语言:javascript
复制
-> print cards 
-> wait for a mouse event 
-> print the card that I've selected with that event. 

现在我想改变程序图形,我已经介绍了QGraphics。我已经创建了一个场景,并在上面打印了所有的对象“卡片”,所以现在我想告诉程序:

代码语言:javascript
复制
-> print the object and wait the mouse input
-> if a card is to selected with the left clik
-> print that card in scene, wait 1/2 second and go ahead with the program

问题是我使用的是for 1到20 (必须在一次匹配中运行20次)。我尝试用随机的G1和COM播放程序,但是应用程序冻结到for的最后一次执行,而我在现场只打印卡片的最后配置。这是因为我之前说过我想让节目停止..。

没有QStateMachine是可以做到的吗?简单地告诉他:“暂停”,打印这个情况,等待鼠标继续前进?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-06-06 22:44:52

下面是一个完整的示例,71行长,在识字编程风格中显示。它也可以在github上使用。该示例由未显示的qmake .pro文件和main.cpp组成,如下所示。该示例具有以下结构:

  1. 标题
  2. 卡片项目
  3. 状态机行为
  4. Main
  5. 页脚

Main

首先,让我们建立我们的场景:

代码语言:javascript
复制
int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   QGraphicsScene scene;
   QGraphicsView view{&scene};
   scene.addItem(new CardItem(0, 0, "A"));
   scene.addItem(new CardItem(20, 0, "B"));

状态机有三个状态:

代码语言:javascript
复制
   QStateMachine machine;
   QState s_idle{&machine};     // idle - no card selected
   QState s_selected{&machine}; // card selected, waiting 1/2 second
   QState s_ready{&machine};    // ready with card selected
   machine.setInitialState(&s_idle);

我们将使用帮助函数以声明方式向机器添加行为。这并不是唯一可能的模式,但它是有效的,并相当容易应用。首先,当选择任何项时,状态从s_idle更改为s_selected

代码语言:javascript
复制
   on_selected(&s_idle, &scene, true, &s_selected);

然后,超时之后,状态更改为s_ready

代码语言:javascript
复制
   on_delay(&s_selected, 500, &s_ready);

如果取消选择项,则返回到s_idle

代码语言:javascript
复制
   on_selected(&s_selected, &scene, false, &s_idle);
   on_selected(&s_ready, &scene, false, &s_idle);

由于我们没有更好的工作要做,所以一旦输入了s_ready状态,我们就可以简单地取消选择所有项。这就清楚地表明这个州已经进入。当然,在清除所选内容后,它将立即保留,并且我们在上面指出,当没有选择任何项时,s_idle是状态。

代码语言:javascript
复制
   QObject::connect(&s_ready, &QState::entered, &scene, &QGraphicsScene::clearSelection);

我们现在可以启动机器并运行我们的应用程序:

代码语言:javascript
复制
   machine.start();

   view.show();
   return app.exec();
}

注意,显式动态内存分配的使用最少,而且没有任何手动内存管理。

卡片项目

CardItem类是一个简单的卡片图形项。项目是可选择的。它也可以是可移动的。交互是由图形视图框架自动处理的:您不需要处理手动解释鼠标按压/拖拽/发布的问题--至少现在还没有。

代码语言:javascript
复制
class CardItem : public QGraphicsObject {
   Q_OBJECT
   const QRect cardRect { 0, 0, 80, 120 };
   QString m_text;
   QRectF boundingRect() const Q_DECL_OVERRIDE { return cardRect; }
   void paint(QPainter * p, const QStyleOptionGraphicsItem*, QWidget*) {
      p->setRenderHint(QPainter::Antialiasing);
      p->setPen(Qt::black);
      p->setBrush(isSelected() ? Qt::gray : Qt::white);
      p->drawRoundRect(cardRect.adjusted(0, 0, -1, -1), 10, 10);
      p->setFont(QFont("Helvetica", 20));
      p->drawText(cardRect.adjusted(3,3,-3,-3), m_text);
   }
public:
   CardItem(qreal x, qreal y, const QString & text) : m_text(text) {
      moveBy(x, y);
      setFlags(QGraphicsItem::ItemIsSelectable);
   }
};

状态机行为

将状态机行为分解为可用于声明给定状态下的行为的函数是很有帮助的。

首先,延迟--一旦输入了src状态,给定的毫秒数就过去了,机器就会过渡到目标状态:

代码语言:javascript
复制
void on_delay(QState * src, int ms, QAbstractState * dst) {
   auto timer = new QTimer(src);
   timer->setSingleShot(true);
   timer->setInterval(ms);
   QObject::connect(src, &QState::entered, timer, static_cast<void (QTimer::*)()>(&QTimer::start));
   QObject::connect(src, &QState::exited,  timer, &QTimer::stop);
   src->addTransition(timer, SIGNAL(timeout()), dst);
}

要拦截选择信号,我们需要一个发出泛型信号的助手类:

代码语言:javascript
复制
class SignalSource : public QObject {
   Q_OBJECT
public:
   Q_SIGNAL void sig();
   SignalSource(QObject * parent = Q_NULLPTR) : QObject(parent) {}
};

然后,我们利用这样的通用信号源来描述当给定场景有选择当且仅当selected为真或没有选择当且仅当selected为假时,向目的地状态转换的行为:

代码语言:javascript
复制
void on_selected(QState * src, QGraphicsScene * scene, bool selected, QAbstractState * dst) {
   auto signalSource = new SignalSource(src);
   QObject::connect(scene, &QGraphicsScene::selectionChanged, signalSource, [=] {
      if (scene->selectedItems().isEmpty() == !selected) emit signalSource->sig();
   });
   src->addTransition(signalSource, SIGNAL(sig()), dst);
}

页眉和页脚

该示例以以下标题开头:

代码语言:javascript
复制
// https://github.com/KubaO/stackoverflown/tree/master/questions/sm-cards-37656060
#include <QtWidgets>

它以下面的页脚结束,由SignalSource类的信号和对象元数据的moc生成的实现组成。

代码语言:javascript
复制
#include "main.moc"
票数 2
EN

Stack Overflow用户

发布于 2016-06-06 14:16:02

在qt中,您不需要积极等待事件(通常也不应该)。只需子类小部件的事件处理方法,它是主接口的一部分。

例如,这是使用QGraphicsItem的子类来更改游戏状态的代码。你也可以对场景本身、小部件等做同样的事情.但通常应该是这样的。

代码语言:javascript
复制
void CardGameGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
   if(event->button() == Qt::RightButton)
   {
      makeQuickChangesToGameState();
      scene()->update(); //ask for a deffered ui update
   }
   QGraphicsItem::mousePressEvent(event);
}

即使您正在以某种方式使用状态机,makeQuickChangesToGameState()也应该触发机器状态更改,并尽快返回。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37656060

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档