实时从数据阅读器终端获取数据到 GUI

Posted

技术标签:

【中文标题】实时从数据阅读器终端获取数据到 GUI【英文标题】:Get data from Data reader terminal to a GUI in real time 【发布时间】:2016-06-16 08:44:41 【问题描述】:

我有一个数据读取器,它从缓冲区获取数据并将输出显示到控制台。现在我想将数据实时查看到文本编辑中。 我已经看到了一些示例,展示了如何从文本文件中读取数据(非实时)。

请查找附件。

/* Create a DataReader for the chatMessageTopic Topic (using the appropriate QoS). */
parentReader = chatSubscriber->create_datareader(
    chatMessageTopic.in(),
    DATAREADER_QOS_USE_TOPIC_QOS,
    NULL,
    STATUS_MASK_NONE);
checkHandle(parentReader.in(), "DDS::Subscriber::create_datareader");

/* Narrow the abstract parent into its typed representative. */
chatAdmin = Chat::ChatMessageDataReader::_narrow(parentReader.in());
checkHandle(chatAdmin.in(), "Chat::ChatMessageDataReader::_narrow");

/* Print a message that the MessageBoard has opened. */
cout << "MessageBoard has opened: send ChatMessages...." << endl << endl;

while (!terminated) 
    /* Note: using read does not remove the samples from
       unregistered instances from the DataReader. This means
       that the DataRase would use more and more resources.
       That's why we use take here instead. */

    status = chatAdmin->take(
        msgSeq,
        infoSeq,
        LENGTH_UNLIMITED,
        ANY_SAMPLE_STATE,
        ANY_VIEW_STATE,
        ALIVE_INSTANCE_STATE );
    checkStatus(status, "Chat::ChatMessageDataReader::take");

    for (DDS::ULong i = 0; i < msgSeq->length(); i++) 
       ChatMessage *msg = &(msgSeq[i]);
       cout << msg->index << ": " << msg->content << endl;
       fflush(stdout);
    

    status = chatAdmin->return_loan(msgSeq, infoSeq);
    checkStatus(status, "Chat::ChatMessageDataReader::return_loan");

【问题讨论】:

QtCreator 是一个 IDE。你正在使用 Qt! 提及您正在使用 RTI Connext C++ API 会有所帮助。 好吧,它是 Prismtech C++ 程序 【参考方案1】:

您可能不希望您的 UI 以 1000Hz 的频率刷新。因此,在繁忙的循环中运行此代码是没有意义的。相反,以合理的刷新率运行它,例如30Hz,来自计时器。

出于性能原因,发出批处理以及日志的单个行可能会有所帮助:

让我们抽象一下:

class DDSProcessor : public QObject 
  Q_OBJECT
  QBasicTimer m_timer;
  DDS::Subscriber * m_chatSubscriber;
  Chat::ChatMessageDataReader * m_chatDataReader = nullptr;
  ...
  Q_SIGNAL void message(const QString &);
  Q_SIGNAL void messages(const QStringList &);
  void timerEvent(QTimerEvent * ev) 
    if (ev->timerId() != m_timer.timerId()) return;
    auto status = chatAdmin->take(
        msgSeq,
        infoSeq,
        LENGTH_UNLIMITED,
        ANY_SAMPLE_STATE,
        ANY_VIEW_STATE,
        ALIVE_INSTANCE_STATE );
    checkStatus(status, "Chat::ChatMessageDataReader::take");
    QStringList texts;
    for (DDS::ULong i = 0; i < msgSeq->length(); i++) 
       auto msg = &(msgSeq[i]);
       auto text = tr("%1: %2").arg(msg->index).arg(msg->content);
       texts << text;
       emit message(text);
    
    emit messages(texts);
    status = chatAdmin->return_loan(msgSeq, infoSeq);
    checkStatus(status, "Chat::ChatMessageDataReader::return_loan");
  
public:
  void start() 
    auto reader = m_chatSubscriber->create_datareader(
      m_chatMessageTopic.in(), ...);
    checkHandle(read.in(), "DDS::Subscriber::create_datareader");
    m_chatDataReader = Chat::ChatMessageDataReader::_narrow(reader.in());
    checkHandler(m_chatDataReader.in(), "Chat::ChatMessageDataReader::_narrow");
    auto text = tr("MessageBoard had opened: send ChatMessages.");
    emit message(text);
    emit messages(text);
    m_timer.start(this, 1000/30);
  
  ...
;

我对 DDS 库不够熟悉,但是您应该使用一些回调来获得通知,然后您会从回调中发出信号。这将消除对显式 UI 更新循环的需要。

那么,您在 UI 中所要做的就是对 DDSProcessor 发出的消息采取行动:

void setup(DDSProcessor * src, QTextEdit * dst) 
  connect(src, &DDSProcessor::message, dst, &QTextEdit::append);

请注意,QTextEditQPlainTextEdit 都非常慢,不适合快速记录。最简单的解决方法是使用带有QListView 的列表模型,并利用批处理日志条目信号:

int main(int argc, char ** argv) 
  QApplication app(argc, argv);
  QStringListModel logModel;
  QListView view;
  DDSProcessor processor;
  QObject::connect(&processor, &DDSProcessor::messages, &logModel, [&](const QStringList & texts)
    auto row = logModel.rowCount();
    logModel.insertRows(row, texts.count());
    for (auto const & text : texts)
      logModel.setData(model.index(row++), text);
  );
  view.setModel(&logModel);
  view.show();
  view.setUniformItemSizes(true); // needed for performance
  ...
  return app.exec();

【讨论】:

【参考方案2】:

这是一个同步记录到 GUI 的最小示例:

int main(int argc, char *argv[]) 
  QApplication app(argc, argv);
  QPlainTextEdit edit;
  edit.show();
  int x = 0;
  while (edit.isVisible()) 
    x++;
    edit.appendPlainText(QString("test %1").arg(x));
    qApp->processEvents();
  
  return 0;

【讨论】:

这太可怕了。它将 CPU 固定在 100%。不要那样做。 一旦你将一些阻塞 IO 操作放入循环中,它就不会 100% 加载 CPU。数据必须来自某个地方。实际上,这种实现可能比基于信号的实现消耗更少的 CPU 资源,因为它不使用计时器并且不需要信号槽系统来工作。但是,如果数据以非常小的块的形式出现,则某种缓冲可能会有所帮助。 GUI线程循环中唯一的阻塞IO操作应该是app.exec()内部的事件等待。其他任何事情都会使响应能力受到影响。在这种情况下,数据来自询问者正在使用的库中的一个单独的工作线程,并且该库也提供回调,所以即使我建议的轮询也不是最好的做法 - 忙碌旋转甚至更糟.

以上是关于实时从数据阅读器终端获取数据到 GUI的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 到 Hadoop 数据的实时摄取

将数据从gui类获取到另一个类

从数据库中获取数据并放入 GUI 中的表中

top在哪里获取实时数据

终端对平台推送拉取数据选型

从 GDAX web socket feed 获取数据