2

シンプルな Qt アプリケーションを開発しようとしています。「START」ボタンを押すと、アプリケーションは継続的にデバイスからデータを取得し (サードパーティのライブラリを使用)、シリアル接続でできるだけ早く転送する必要があります。

私が今まで使用していた (醜い) ソフトウェアは、次のサイクルを使用して、順次実行され、ホストによって利用可能になるとすぐにデータ フレームを取得するコンソール アプリケーションでした。

    while(1)
    {
          [...]
        while( MyClient.GetFrame().Result != Result::Success )
        {
            Sleep( 200 );
            std::cout << ".";
        }

          [... pack and send on serial]
    }

GUIの応答性を維持するだけでなく、getFrameとシリアル書き込み機能の間のレイテンシを最小限に抑えるために、Qtでこれを実装するより便利な方法はどれか疑問に思っていました。

タイマーでトリガーされる SLOTを使用する必要がありますか? QtConcurrent 名前空間? QRunnable ? これらの各アプローチの主な長所と短所はどれですか?

ご協力いただきありがとうございます!

4

1 に答える 1

1

既存のライブラリではデータをポーリングする必要があるため、タイマーで実行するしかありません。このジョブを実行するオブジェクトをメイン スレッドで実行するか、ワーカー スレッドで実行するかは、ユーザーが選択できます。Qt Concurrent や QRunnable を使用する必要はありません。QObject を使用すると、GUI にフィードバックを簡単に提供できるため、作業がいくらか簡単になります。

たとえば、クライアントの API について次のように仮定します。

class Worker : public QObject {
  Client m_client;
  QSerialPort m_port;
  QBasicTimer m_timer;

  void processFrame() {
    if (m_client.GetFrame().Result != Result::Success) return;
    QByteArray frame = QByteArray::fromRawData(
      m_client.GetFrame().Data, m_client.GetFrame().Size);
    ... process the frame
    if (m_port.write(frame) != frame.size()) {
      ... process the error
    }
  }
  void timerEvent(QTimerEvent * ev) {
    if (ev->timerId() == m_timer.timerId()) processFrame();
  }
public:
  Worker(QObject * parent = 0) : QObject(parent) {}
  Q_SLOT bool open(const QString & name) {
    m_port.close();
    m_port.setPortName(name);
    if (!m_port.open(name)) return false;
    if (!m_port.setBaudRate(9600)) return false;
    if (!m_port.setDataBits(QSerialPort::Data8)) return false;
    ... other settings go here
    return true;
  }
  Q_SLOT void start() { m_timer.start(200, this); }
  Q_SLOT void stop() { m_timer.stop(); }
  ...
}

/// A thread that's always safe to destruct
class Thread : public QThread {
  using QThread::run; // lock the default implementation
public:
  Thread(QObject * parent = 0) : QThread(parent) {}
  ~Thread() { quit(); wait(); }
};

int main(int argc, char ** argv) {
  QApplication app(argc, argv);
  Worker worker;
  Thread thread; // worker must be declared before thread!
  if (true) {
    // this code is optional, disabling it should not change things
    // too much unless the client implementation blocks too much
    thread.start();
    worker.moveToThread(&thread);
  }

  QPushButton button("Start");
  QObject::connect(&button, &QPushButton::clicked, [&worker]{
    // Those are cross-thread calls, they can't be done directly
    QMetaObject::invoke(&worker, "open", Q_ARG(QString, "COM1");
    QMetaObject::invoke(&worker, "start");
  });
  button.show();

  return app.exec(argc, argv);
}
于 2014-03-27T21:09:57.683 に答える