在Qt中计时事件的最佳方法

3

我正在使用Qt/c++编写一款软件,通过串口与Arduino和其他电子设备进行通信。

我需要启动一个事件序列,以不同的时间调用不同的槽,就像这样:

  • 启动电机1 20秒
  • 在10秒后启动电机2
  • 停止电机1
  • 更改电机1速度
  • 启动电机1 30秒

我尝试使用QTimer :: singleShot,但它仅适用于没有参数的插槽,并且我需要设置不同于时间的电机速度等参数。

现在我正在使用延迟函数,将当前时间与死亡时间进行比较,但是跟踪所有设备的定时非常复杂。

有什么最好的解决方案吗?建议?


如果静态函数QTimer::singleShot对您不起作用,那么使用设置为单次触发的QTimer实例如何?(http://doc.qt.io/qt-5/qtimer.html#singleShot-prop) - TheDarkKnight
2
QTimer确实是最好的解决方案。你可以通过类的字段传递参数。 - Amartel
@Amartel,我正在尝试通过类的字段传递参数,但我的问题是如何根据时间传递参数。例如,如果我想将电机设置为x速度,启动电机30秒钟,停止电机,将其设置为y速度,然后再次启动,如何根据时间更新参数?我正在考虑将所有参数排队,但我认为这不是一个好的解决方案。 - nicodio
2个回答

4
如果您使用重载方法(接受函数对象),则可以使用静态QTimer单次触发功能。这将允许您捕获所需的变量,例如电机、速度、动作等。如果您不熟悉函数对象,可以在此处阅读有关它们的信息。
或者,由于问题没有提供代码示例,让我们假设您已经编写了启动和停止电机的函数。对于更直接的方法,使用C++11,您可以像这样做:
StartMotor(1);

// Stop in 20 seconds
QTimer* p1 = new QTimer;
connect(p1, &QTimer::timeout, [=]{
    StopMotor(1);
    p1->deleteLater(); // clean up
});
p1->start(1000 * 20); // trigger in 20 seconds

// After 10 seconds, start motor 2

QTimer* p2 = new QTimer;
connect(p2, &QTimer::timeout, [=]{
    StartMotor(2);

    // And Stop Motor 1
    StopMotor(1);

    p2->deleteLater(); // clean up
});
p2->start(1000 * 10); // trigger in 10 seconds

...每个定时动作都是如此。


使用lambda作为槽/回调看起来非常优雅。实际上没有必要定义每个计时器。QTimer *p; 和 p = new QTimer; 每次都可以。 - Valentin H
@ValentinHeinitz,当然可以,不过我在这里为了清晰起见定义了单独的计时器。 - TheDarkKnight
我接受了这个答案,因为它是我现在需要做的。唯一的问题是,如果有很多指令,代码可能会有点难以阅读,但我的工作就是尽力写好它。谢谢。 - nicodio
如果可读性成为问题,我建议考虑使用Functor来封装每个计时器的操作。或者,您可以创建诸如Action1、Action2、Action3等函数,然后编写一个函数,该函数接受指向动作函数和时间延迟的指针。然后,该函数创建计时器并调用start,使用通过函数指针传递的给定函数的lambda进行调用。 - TheDarkKnight

3

实现一个电机的类,例如:

class Motor:public QObject
{
  Q_OBJECT
  public slots:
    void start();
    void start(int ms); //this one starts QTimer::singleShot and calls stop
    void stop();
};

我建议检查 QStateMachine框架。当任务变得更加复杂时,使用FSM比信号槽调用的意大利面条更好。
在我的当前项目中,我基于QStateMachine构建了一个基于DSL(领域特定语言)的FSM定义。

在这个解决方案中,如果我从主类执行以下操作:motor1->start(10); motor1->setSpeed(100); motor1->start(20); 它只会执行第一个命令,因为当我调用第二个和第三个命令时,第一个命令仍在执行。我需要创建某种队列来保留所有发送的命令吗? - nicodio
1
取决于你想要什么。如果命令应该异步地一个接一个地执行,那么排队是唯一的解决方案。可以将字符串作为命令排队,并在前一个命令完成后解释它们,或者使用命令模式 - 将命令封装在一个类中。 - Valentin H
谢谢。我选择了其他答案,因为它更符合我的问题。但是我也会使用您的建议来编写更好、更可用的代码。 - nicodio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接