QT非交互UI值更新

QT non-interactive UI value updation

我设计了一个非常简单的 qt 应用程序(UI 小部件),它在文本框中显示 3 个浮点值。有一个 main 函数(QT 初学者喜欢的 tutorial)做所有的处理和一个带有 3 个文本框的 QMainWindow class,在这个 QMainWindow 中通过 setter 方法设置;这些方法是从主函数调用的,我只是传递传感器读数。它应该做的就是不断更新 UI 上的值,因为它们在后台进程中发生变化。 这个后台进程是一个处理传感器硬件的 c++ 项目可执行文件,由 QProcess 对象调用;这个可执行文件以大约 20 Hz 的频率不断地计算出 3 个数字(这些是传感器读数并且它们一直在波动)。我的所有逻辑在 QApplication 的主函数中都正常工作,我可以 qDebug() 并看到正在输出正确的值。

我唯一的问题是更新 UI 小部件中显示的值。我昨天开始研究 QT,所以我是新手,我阅读了有关插槽和信号的信息,但我并不真正需要事件处理。我只需要更新 textEdit 值。

实际值处理是在传感器读取处理循环中完成的;这是一个无限的 while 循环,它将不断从可执行文件中喷出值,直到我退出循环:

1) 如果我在这个 while 循环中调用 app.exec()(一开始这听起来像是一个非常糟糕的主意,因为我认为它会继续创建新的 windows),什么也不会发生; UI 出现,值不断在后台生成,但不显示在 UI.

2) 如果我在 while 循环之前调用 app.exec(),这是正确的方法,则 UI 中不会显示任何值,但可执行文件会继续在后台生成(类似于(1))。这是我真正的问题;如何在调用 app.exec() 后刷新 UI 中的值?

3)如果我在这个循环之后调用 app.exec(),它将只显示一组值,因为我已经跳出循环。

为此,我在 google 上阅读了有关使用槽和信号进行事件处理的内容(大多数论坛都推荐这样做)。尽管我真的没有包含多个对象的复杂应用程序。 我也尝试过使用指针,但是在我处理了所有运行时异常之后,这些值仍然没有得到更新。我无法 post 此处的代码,因为代码位于嵌入式设备上,目前无法连接到互联网。

我正在使用 process.start(programPath, arguments) 启动进程,并使用 p.waitForReadyRead()p.readLine() 从可执行文件的控制台输出中读取数据 有什么简单的想法可以让我这样做吗?提前致谢

Qt 对 GUI 使用 MVC 方法。 Signal/slots 触发视图的更新,而您的模型(您的 textEdit)由控制器(您的 class,另一个组件)更新。 您应该始终使用 signal/slots 以获得响应式 GUI 并更好地处理您的应用程序

作为新手,忘记所有名为 waitFor... 的方法会很有帮助。它们会阻塞,并且在 gui 应用程序中,这通常会使事情无法正常工作。你不需要这些方法,所以不要使用它们。

QProcess 有新的输入可用时,您需要做出反应。下面是 Qt 5 和 C++11 的简单 stand-alone 示例。

如果使用任何命令行参数调用该应用程序,它会充当一个生成器来模拟您的数据源进程。当不带参数调用时,它将以仿真模式自行启动,并显示实时反映传入数据的用户界面。

有几点值得一提:

  1. 它是一个 single-file 示例,它会自己调用一次 :)
  2. 没有一个明确的内存分配。应避免手动内存管理。所有对象的正确生命周期由 C++11 和 Qt 的语义处理。
  3. QCoreApplication::applicationFilePath()用于引用可执行文件。
  4. 当进程指示有新数据可用时,我们读取任何数据,只要能读取整行即可。只要你使用readLine,你就必须这样做。 QProcess 可以指示任何块中的数据 - 不能保证任何数量的行都可用。当发出 readyRead 时,您可以看到任意数量的行,包括零行! readyRead 的意思是至少有一个字节的数据要读取。就这些了。
  5. Lambda 用于保持代码简洁。 Qt 4 风格的代码至少需要一个 QObject 派生的 class 来提供必要的插槽。
  6. 为了正确关闭进程,应用程序不会在最后一个 window 关闭时退出 - 默认情况下会。当最后一个window关闭时,进程被请求终止,当它结束时,应用程序退出。
  7. 同样,这是与 Python 或其他 high-level 脚本语言一样简洁的 C++11 代码。这就是 Qt 为 C++11 带来的。它不应该像 C 那样读,它不应该 :)

#include <QApplication>
#include <QGridLayout>
#include <QProcess>
#include <QLabel>
#include <QTimer>
#include <QTextStream>
#include <QRegExp>
#include <cstdio>

// QT 5, C++11
int main(int argc, char *argv[])
{
   if (argc > 1) {
      QCoreApplication app(argc, argv);
      // output 3 random values per line at ~20Hz
      QTextStream out(stdout);
      QTimer timer;
      timer.start(50);
      QObject::connect(&timer, &QTimer::timeout, [&out]{
         out << qrand() << " " << qrand() << " " << qrand() << endl;
      });
      return app.exec();
   }
   QApplication app(argc, argv);
   QWidget w;
   QGridLayout layout(&w);
   QLabel l1, l2, l3;
   layout.addWidget(&l1, 0, 0);
   layout.addWidget(&l2, 0, 1);
   layout.addWidget(&l3, 0, 2);
   QProcess process;
   process.start(QCoreApplication::applicationFilePath(), QStringList("foo"));
   QObject::connect(&process, &QProcess::readyRead, [&]{
      static QRegExp sep("\W+");
      while (process.canReadLine()) {
         QStringList data = QString::fromLocal8Bit(process.readLine()).split(sep, QString::SkipEmptyParts);
         if (data.length() != 3) continue;
         l1.setText(data.at(0));
         l2.setText(data.at(1));
         l3.setText(data.at(2));
      }
   });
   app.setQuitOnLastWindowClosed(false);
   process.connect(&app, SIGNAL(lastWindowClosed()), SLOT(terminate()));
   app.connect(&process, SIGNAL(finished(int)), SLOT(quit()));
   w.show();
   return app.exec();
}
#include <QApplication>
#include <QGridLayout>
#include <QProcess>
#include <QLabel>
#include <QTimer>
#include <QTextStream>
#include <QRegExp>
#include <QPointer>
#include <cstdio>

// QT 4, C++98
class Emulator : public QObject {
   Q_OBJECT
   QTextStream m_out;
   QTimer m_timer;
   Q_SLOT void on_timeout() {
      m_out << qrand() << " " << qrand() << " " << qrand() << endl;
   }
public:
   Emulator() : m_out(stdout) {
      m_timer.start(50);
      connect(&m_timer, SIGNAL(timeout()), SLOT(on_timeout()));
   }
};

class Widget : public QWidget {
   Q_OBJECT
   QGridLayout m_layout;
   QLabel m_l1, m_l2, m_l3;
   QPointer<QProcess> m_process;
   Q_SLOT void on_readyRead() {
      static QRegExp sep("\W+");
      while (m_process->canReadLine()) {
         QStringList data = QString::fromLocal8Bit(m_process->readLine()).split(sep, QString::SkipEmptyParts);
         if (data.length() != 3) continue;
         m_l1.setText(data.at(0));
         m_l2.setText(data.at(1));
         m_l3.setText(data.at(2));
      }
   }
public:
   Widget(QProcess * process) : m_layout(this), m_process(process) {
      m_layout.addWidget(&m_l1, 0, 0);
      m_layout.addWidget(&m_l2, 0, 1);
      m_layout.addWidget(&m_l3, 0, 2);
      connect(m_process, SIGNAL(readyRead()), SLOT(on_readyRead()));
   }
};

int main(int argc, char *argv[])
{
   if (argc > 1) {
      // output 3 random values per line at ~20Hz
      QCoreApplication app(argc, argv);
      Emulator emulator;
      return app.exec();
   }
   QApplication app(argc, argv);
   QProcess process;
   Widget w(&process);
   process.start(QCoreApplication::applicationFilePath(), QStringList("foo"));
   app.setQuitOnLastWindowClosed(false);
   process.connect(&app, SIGNAL(lastWindowClosed()), SLOT(terminate()));
   app.connect(&process, SIGNAL(finished(int)), SLOT(quit()));
   w.show();
   return app.exec();
}
#include "main.moc"