2015-07-13 7 views
1

Моя проблема заключается в следующем: мне нужно создать класс, содержащий экземпляр QStateMachine. Этот класс должен иметь слоты, через которые вы могли бы «спросить» конечный автомат, чтобы перейти в другое состояние. И если переход был успешным, мой класс должен излучать сигнал об этом. Как бы это реализовать? Класс должен иметь возможность испускать определенные сигналы в соответствии с определенным вызовом слота. Вот небольшой пример класса:Излучение сигналов от класса, если переход в QStateMachine прошел успешно

class MyClass : public QObject 
{ 
    Q_OBJECT 
public: 
    explicit MyClass(QObject *parent = 0) 
    { 
     mStateMachine = new QStateMachine(this); 
     QState *s1 = new QState(mStateMachine); 
     QState *s2 = new QState(mStateMachine); 
     QState *s3 = new QState(mStateMachine); 

     s1->addTransition(); // Transition to s2 
     s2->addTransition(); // Transition to s3 
     s3->addTransition(); // Transition to s1 

     mStateMachine->setInitialState(s1); 
     mStateMachine->start(); 
    } 

signals: 
    toS1(); 
    toS2(); 
    toS3(); 

public slots: 
    slotToS1() 
    { 
     /* post event to state machine about 
     transition to state s1, 
     if transition was successful, 
     then emit toS1() signal. */ 
    }; 
    slotToS2(){ /* Similar to slotToS1 */}; 
    slotToS3(){ /* Similar to slotToS1 */}; 
private: 
    QStateMachine *mStateMachine; 
} 

Я был бы очень благодарен за вашу помощь!

UPD:
Щели представляют defferent виды переходов, так что внешний класс (который будет использовать MyClass) может «спросить» в течение некоторого переходного периода. Таким образом, событие отправки сигнала или сигнал на конечный автомат, он смотрит на событие или сигнал и (если в правильном состоянии) делает этот переход. И я хочу уведомить внешний класс с определенным сигналом, который спросил, прежде чем слот (переход) был успешно выполнен.

ответ

3
  1. Для перехода на слот вызова, вам нужно как-то привяжите слот к QAbstractTransition. Есть два способа сделать это:

    • Используйте QEventTransition и отправить соответствующее событие, чтобы вызвать его.

    • Используйте QSignalTransition и используйте внутренний сигнал для его запуска.

  2. Для того, чтобы испускать сигналы на переходах состояний, можно подключить сигналы к QAbstractTransition::triggered или QState::entered или QState::exited к других сигналов. Помните, что в Qt целью соединения может быть либо слот, либо сигнал.

Таким образом, используя сигнальные переходы:

class MyClass : public QObject 
{ 
    Q_OBJECT 
    QStateMachine machine; 
    QState s1, s2; 
    Q_SIGNAL void s_go_s1_s2(); 
    Q_SIGNAL void s_go_s2_s1(); 
public: 
    Q_SIGNAL void transitioned_s1_s2(); 
    Q_SIGNAL void transitioned_s2_s1(); 
    Q_SLOT void go_s2_s1() { emit s_go_s2_s1(); } 
    Q_SLOT void go_s1_s2() { emit s_go_s1_s2(); } 
    explicit MyClass(QObject *parent = 0) : QObject(parent), 
    s1(&machine), s2(&machine) { 
    auto s1_s2 = s1.addTransition(this, SIGNAL(s_go_s1_s2()), &s2); 
    auto s2_s1 = s2.addTransition(this, SIGNAL(s_go_s2_s1()), &s1); 
    machine.setInitialState(&s1); 
    machine.start(); 
    connect(s1_s2, &QAbstractTransition::triggered, this, &MyClass:: transitioned_s1_s2); 
    connect(s2_s1, &QAbstractTransition::triggered, this, &MyClass:: transitioned_s2_s1); 
    } 
} 

Использование переходов события немного сложнее, так как события, которые вы используете, должны быть клонируемыми государственной машиной. Конечный автомат основного модуля знает, как клонировать события None и Timer - см. Его реализацию cloneEvent.

Модуль виджетов добавляет поддержку различных событий GUI/Widgets - см. Реализацию cloneEvent.Вы могли бы, в крайнем случае, использовать такие графические события для своих собственных целей - в конце концов, они отправляются на равнину QObject, которая не интерпретирует их особым образом.

Вы можете задать свой собственный вариант cloneEvent, который ссылается на других.

#include <private/qstatemachine_p.h> 

class MyClass : public QObject 
{ 
    Q_OBJECT 
    QStateMachine machine; 
    QState s1, s2; 
    QEvent e_s1_s2, e_s2_s1; 
    QEventTransition s1_s2, s2_s1; 
public: 
    Q_SIGNAL void transitioned_s1_s2(); 
    Q_SIGNAL void transitioned_s2_s1(); 
    Q_SLOT void go_s2_s1() { QCoreApplication::sendEvent(this, &e_s2_s1); } 
    Q_SLOT void go_s1_s2() { QCoreApplication::sendEvent(this, &e_s1_s2); } 
    explicit MyClass(QObject *parent = 0) : QObject(parent), 
    s1(&machine), s2(&machine), 
    e_s1_s2((QEvent::Type)(QEvent::User + 1)), 
    e_s2_s1((QEvent::Type)(QEvent::User + 2)), 
    s1_s2(this, e_s1_s2.type()), 
    s2_s1(this, e_s2_s1.type()) { 
    s1_s2.setTargetState(&s2); 
    s2_s1.setTargetState(&s1); 
    s1.addTransition(&s1_s2); 
    s2.addTransition(&s2_s1); 
    machine.setInitialState(&s1); 
    machine.start(); 
    connect(&s1_s2, &QAbstractTransition::triggered, this, &MyClass::transitioned_s1_s2); 
    connect(&s2_s1, &QAbstractTransition::triggered, this, &MyClass::transitioned_s2_s1); 
    } 
} 

static const QStateMachinePrivate::Handler * last_handler = 0; 

static QEvent * cloneEvent(QEvent * e) { 
    if (e->type() >= QEvent::User && e->type() < QEvent::User+100) { 
    return new QEvent(e->type()); 
    return last_handler->cloneEvent(e); 
} 

const QStateMachinePrivate::Handler our_handler = { 
    cloneEvent 
}; 

void registerHandler() { 
    last_handler = QStateMachinePrivate::handler; 
    QStateMachinePrivate::handler = &our_handler; 
} 
Q_CONSTRUCTOR_FUNCTION(registerHandler()) 

void unregisterHandler() { 
    QStateMachinePrivate::handler = last_handler; 

} 
Q_DESTRUCTOR_FUNCTION(unregisterHandler()) 
+0

Это не совсем мое дело. Я хочу запускать сигналы, когда происходят переходы, но не при входе в какое-то состояние. Я обновил вопрос. –

+1

@NazariiPlebanskii Вы также можете использовать сигнал «triggered» самого перехода, если есть несколько переходов, ведущих к состоянию. –

+0

действительно. Это было бы супер для меня! Благодарю. –

2

У меня была такая же проблема в прошлом, и я нашел, что самый простой способ - наследовать fom QState своим классом QState и реализовать 2 метода, называемых QState :: onEntry (событие QEvent *) QState :: onExit (QEvent * event)

Таким образом, вы можете излучать любой сигнал, который вам нравится, когда вы выходите и когда вы входите в новое состояние.

Вот и пример:

файл mystate.h

#include <QState> 

    class MyState : public QState 
    { 
     Q_OBJECT 
    public: 
     explicit MyState(qint32 stateId, QState * parent = 0); 

    protected: 
     void onEntry(QEvent * event); 
     void onExit(QEvent * event); 

    signals: 
     void exit(qint32 stateId); 
     void enter(qint32 stateId); 

    private: 

     qint32 stateId; 

    }; 

И файл mystate.cpp

#include "mystate.h" 


MyState::MyState(qint32 stateId, QState *parent) 
{ 
    this->stateId = stateId; 
} 

void MyState::onEntry(QEvent *event) 
{ 
    emit enter(stateId); 
} 

void MyState::onExit(QEvent *event) 
{ 
    emit (exit(stateId)); 
} 
+1

Это не нужно. Вы можете подключить сигналы 'enter' и' exited' к другим сигналам в Qt 4 и использовать 'QObject :: sender()', чтобы определить, какое состояние было источником сигнала. В Qt 5 вы можете подключить сигналы к lambdas, которые излучают сигналы с данными, которые вы желаете. –