【问题标题】:Run a timer for 5 seconds in Qt在 Qt 中运行一个 5 秒的计时器
【发布时间】:2016-04-22 18:25:42
【问题描述】:

我正在尝试使用 QTimer 函数运行一个函数大约 5 秒钟。在研究了文档和测试之后,我似乎无法在 Qt 中找到执行此操作的函数。

我尝试了以下方法:

QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(myFunction());
    timer->start(5000);

这种方法每 5 秒运行一次函数。这不是我想要达到的目标。我还尝试使用以下代码使用singleShot() 属性:

QTimer::singleShot(5000,this,SLOT(myFunction()));

这个函数只会触发我的函数一次。 QTimer 是否有任何属性可以在给定时间内运行我的函数。比如:

run function and timeout function after 5 seconds.

编辑:更多信息: 这是我大学的机器人比赛。我开车要做的是通过 TCP 驱动机器人的轮子。我知道两个轮子需要运行多少秒才能让机器人转动某个角度。 myFunction 中的 SLOT 类似于 senddatatoRobot() 该功能基本上通过 TCP 将数据发送到我的机器人,根据机器人从所需端点的方向将其转动一定角度。例如,如果我向左右轮发送 4096 和 0 的 PWM 值 5 秒,我知道我的机器人将进行 360 度转弯。因此,以 45 度转弯为例,我会在指定的秒数内发送数据。

【问题讨论】:

  • 函数的运行时间取决于它在做什么。请解释一下,为什么你需要这些东西?看起来你在谈论实时操作系统。
  • @SaZ,我正在通过 wifi 驱动机器人的轮子。我知道两个轮子需要运行多少秒才能让机器人转动某个角度。这就是我要在这里实现的。
  • 我认为你的意思是 interupt 5 秒后你的函数。您可以在函数本身中通过轮询、通过停止线程或使用系统级信号中断来使用线程。这不是 Qt 的事情。
  • @S.Pinkus,你说得对,我的措辞可能不是那么好。
  • 我怀疑你需要重复发送数据。发送 (4096,0) 一次 让车轮转动,然后发送 (0,0) 一次 让它们停下来。

标签: c++ qt


【解决方案1】:

你可以试试这样的:

QElapsedTimer t;
t.start();

while (t.elapsed() < 5000) { ...do stuff... } // this will block the thread

你也可以这样做:

startDoingStuff(); // must not block the thread
QTimer::singleShot(5000, this, "stopDoingStuff");

但是从您的 cmets 看来,您需要分享该函数的实际作用,并且肯定会更多地了解事件驱动编程,然后您将能够提出更好的设计。

该功能基本上是通过 TCP 向我的机器人发送数据,将其变为 基于机器人从所需方向的特定角度 端点。例如,我知道我的机器人会做 360 度转弯,如果 我向左右轮发送 4096 和 0 的 PWM 值 5 秒。例如,要转 45 度,我会输入 send 指定秒数的数据。

在我看来,更有效的解决方案是只发送值更改而不是重复发送相同的数据。

因此,在机器人的微控制器代码中,您将持续缓存和使用这些值,并且在您的控制器应用程序中,您只会“设置”远程值:

public slots:
    void reset() { send(0, 0); }
...
// begin turning
send(4096, 0);
QTimer::singleShot(5000, this, "reset"); // end turning in 5 secs

更好的解决方案是发送机器人特定命令而不是发送 PWM 值,并让机器人的微控制器保持计时,因为它很可能比QTimer 更擅长。您甚至可以获得更具体的信息,例如在 T 秒内旋转 X 度的命令。在机器人微控制器代码方面,您肯定有很大的改进空间。

如果您必须使用您当前正在尝试的方法,您可以使用QElapsedTimer 方法来实现具有函数void send(v1, v2, time) 的worker,只要将worker 放在另一个线程中即可,所以你不会阻塞主线程,这会导致操作系统将你的应用程序报告为“没有响应”。但是这种方式命令不能被打断,在当前命令完成之前不能发出新的命令。如this example 中所述,您可以通过实施非阻塞工作者来改进它。这样,您就可以在当前命令完成之前通过中断发出新命令。

【讨论】:

  • 该函数基本上是通过 TCP 向我的机器人发送数据,并根据机器人从所需端点的方向将其旋转一定角度。例如,如果我向左右轮发送 4096 和 0 的 PWM 值 5 秒,我知道我的机器人将进行 360 度转弯。因此,以 45 度转弯为例,我会输入指定秒数的发送数据。
  • 我最终采纳了你的建议。 你发送你的机器人特定命令,让机器人的微控制器保持计时,因为它很可能比 QTimer 更擅长
【解决方案2】:

您尚未指定如何指挥您的机器人。有两种方法:

  1. 在间隔开始时发送 ON 命令,然后在间隔结束时发送 OFF 命令;

  2. 在间隔期间重复发送 ON 命令,电机在收到最后一个命令后“不久”关闭,没有明确的 OFF 命令。

使用状态机很容易实现这两种方法。让我们看看如何实现第一种方法。

控制器可以封装行为。 isIdleisActive 信号将连接到向您的机器人发送命令的操作。

class Controller : public QObject {
   Q_OBJECT
   QStateMachine m_machine{this};
   QState
      m_idle    {&m_machine},
      m_active  {&m_machine};
   Transition
      m_go{&m_idle, &m_active};
   Delay
      m_activeTime{&m_active, &m_idle, 0};
public:
   Controller(QObject * parent = 0) : QObject(parent) {
      connect(&m_idle, &QState::entered, this, &Controller::isIdle);
      connect(&m_active, &QState::entered, this, &Controller::isActive);
      m_machine.setInitialState(&m_idle);
      m_machine.start();
   }
   Q_SLOT void moveFor(int ms) {
      m_activeTime.setDuration(ms);
      m_go();
   }
   Q_SIGNAL void isIdle();
   Q_SIGNAL void isActive();
};

TransitionDelay 类实现与状态转换相关的行为:

// https://github.com/KubaO/stackoverflown/tree/master/questions/robot-state-timer-36769933
#include <QtWidgets>

struct Transition : public QObject {
   Q_OBJECT
public:
   Transition(QState * source, QState * destination) : QObject(source->machine()) {
      source->addTransition(this, &Transition::trigger, destination);
   }
   Q_SIGNAL void trigger();
   void operator()() { trigger(); }
};

class Delay : public Transition {
   Q_OBJECT
   int m_duration;
   QBasicTimer m_timer;
   void timerEvent(QTimerEvent * ev) {
      if (m_timer.timerId() != ev->timerId()) return;
      m_timer.stop();
      trigger();
   }
public:
   Delay(QState * src, QState * dst, int ms) : Transition(src, dst), m_duration(ms) {
      connect(src, &QState::entered, this, [this]{ m_timer.start(m_duration, this);});
   }
   Q_SLOT void setDuration(int duration) { m_duration = duration; }
};

测试工具可以使示例完整。

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   Controller ctl;
   QWidget w;
   QFormLayout layout{&w};
   QPushButton start{"Go"};
   QLineEdit duration{"5000"};
   QPlainTextEdit log;
   log.setReadOnly(true);
   layout.addRow("Duration", &duration);
   layout.addRow(&start);
   layout.addRow(&log);

   QObject::connect(&ctl, &Controller::isIdle, &log, [&]{ log.appendPlainText("Idle"); });
   QObject::connect(&ctl, &Controller::isActive, &log, [&]{ log.appendPlainText("Active"); });
   QObject::connect(&start, &QPushButton::clicked, &ctl, [&]{
      ctl.moveFor(duration.text().toInt());
   });
   w.show();
   return app.exec();
}

#include "main.moc"

【讨论】:

    猜你喜欢
    • 2023-04-02
    • 2018-09-10
    • 1970-01-01
    • 2014-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    相关资源
    最近更新 更多