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 示例。
如果使用任何命令行参数调用该应用程序,它会充当一个生成器来模拟您的数据源进程。当不带参数调用时,它将以仿真模式自行启动,并显示实时反映传入数据的用户界面。
有几点值得一提:
- 它是一个 single-file 示例,它会自己调用一次 :)
- 没有一个明确的内存分配。应避免手动内存管理。所有对象的正确生命周期由 C++11 和 Qt 的语义处理。
QCoreApplication::applicationFilePath()
用于引用可执行文件。
- 当进程指示有新数据可用时,我们读取任何数据,只要能读取整行即可。只要你使用
readLine
,你就必须这样做。 QProcess
可以指示任何块中的数据 - 不能保证任何数量的行都可用。当发出 readyRead
时,您可以看到任意数量的行,包括零行! readyRead
的意思是至少有一个字节的数据要读取。就这些了。
- Lambda 用于保持代码简洁。 Qt 4 风格的代码至少需要一个
QObject
派生的 class 来提供必要的插槽。
- 为了正确关闭进程,应用程序不会在最后一个 window 关闭时退出 - 默认情况下会。当最后一个window关闭时,进程被请求终止,当它结束时,应用程序退出。
- 同样,这是与 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"
我设计了一个非常简单的 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 示例。
如果使用任何命令行参数调用该应用程序,它会充当一个生成器来模拟您的数据源进程。当不带参数调用时,它将以仿真模式自行启动,并显示实时反映传入数据的用户界面。
有几点值得一提:
- 它是一个 single-file 示例,它会自己调用一次 :)
- 没有一个明确的内存分配。应避免手动内存管理。所有对象的正确生命周期由 C++11 和 Qt 的语义处理。
QCoreApplication::applicationFilePath()
用于引用可执行文件。- 当进程指示有新数据可用时,我们读取任何数据,只要能读取整行即可。只要你使用
readLine
,你就必须这样做。QProcess
可以指示任何块中的数据 - 不能保证任何数量的行都可用。当发出readyRead
时,您可以看到任意数量的行,包括零行!readyRead
的意思是至少有一个字节的数据要读取。就这些了。 - Lambda 用于保持代码简洁。 Qt 4 风格的代码至少需要一个
QObject
派生的 class 来提供必要的插槽。 - 为了正确关闭进程,应用程序不会在最后一个 window 关闭时退出 - 默认情况下会。当最后一个window关闭时,进程被请求终止,当它结束时,应用程序退出。
- 同样,这是与 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"