如何异步与儿童过程进行沟通?
我有一个由GTKMM构建的父级应用程序,我需要产生一个子过程(另一个GUI应用程序)并与之通信。我使用boost ::流程来做到这一点。我知道我应该异步进行,以便不会阻止父ui。
因此,问题:
- 如何异步聆听儿童应用程序中的任何输出并处理它?
- 我怎么知道何时关闭/终止儿童应用程序/过程?
这是我当前这样做的方式(正在阻止UI):
#include <iostream>
#include <boost/process.hpp>
#include <gtkmm.h>
using namespace std;
using namespace boost::process;
class MyWindow : public Gtk::Window
{
public:
MyWindow();
private:
Gtk::Button *start_btn;
void Start();
};
void MyWindow::Start() {
// The target app is built from .NET 5.0 to run on RPi (linux-arm)
ipstream pipe_stream;
// change to your own target process
child c("/usr/bin/dotnet", "/home/pi/updater/Updater.dll", std_out > pipe_stream);
std::string line;
bool upToDate;
while (pipe_stream && std::getline(pipe_stream, line) && !line.empty()) {
std::cout << line << std::endl;
try {
upToDate = line == "True" || line == "true" || line == "1";
if (upToDate) {
std::cout << "up-to-date" << std::endl;
break;
}
else {
std::cout << "update available!" << std::endl;
break;
}
}
catch(exception& e) {
std::cerr << e.what() << std::endl;
}
}
c.wait();
}
MyWindow::MyWindow()
{
set_title("Basic application");
set_default_size(200, 200);
start_btn = Gtk::make_managed<Gtk::Button>("Start process");
start_btn->signal_clicked().connect(sigc::mem_fun(*this, &MyWindow::Start));
this->add(*start_btn);
this->show_all();
}
int main(int argc, char* argv[])
{
auto app = Gtk::Application::create("org.gtkmm.examples.base");
MyWindow win;
return app->run(win);
}
此代码使用GTKMM 3.0 LIB
I have a parent GUI app built with GTKmm, and I need to spawn a child process (another GUI app) and communicate with it. I use boost::process to do that. I know that I should do it asynchronously, so that the parent UI wouldn't be blocked.
So the questions:
- How can I asynchronously listen to any output from the child app and process it?
- How can I know when the child app/process has been closed/terminated?
here is how I currently do it (which is blocking the UI):
#include <iostream>
#include <boost/process.hpp>
#include <gtkmm.h>
using namespace std;
using namespace boost::process;
class MyWindow : public Gtk::Window
{
public:
MyWindow();
private:
Gtk::Button *start_btn;
void Start();
};
void MyWindow::Start() {
// The target app is built from .NET 5.0 to run on RPi (linux-arm)
ipstream pipe_stream;
// change to your own target process
child c("/usr/bin/dotnet", "/home/pi/updater/Updater.dll", std_out > pipe_stream);
std::string line;
bool upToDate;
while (pipe_stream && std::getline(pipe_stream, line) && !line.empty()) {
std::cout << line << std::endl;
try {
upToDate = line == "True" || line == "true" || line == "1";
if (upToDate) {
std::cout << "up-to-date" << std::endl;
break;
}
else {
std::cout << "update available!" << std::endl;
break;
}
}
catch(exception& e) {
std::cerr << e.what() << std::endl;
}
}
c.wait();
}
MyWindow::MyWindow()
{
set_title("Basic application");
set_default_size(200, 200);
start_btn = Gtk::make_managed<Gtk::Button>("Start process");
start_btn->signal_clicked().connect(sigc::mem_fun(*this, &MyWindow::Start));
this->add(*start_btn);
this->show_all();
}
int main(int argc, char* argv[])
{
auto app = Gtk::Application::create("org.gtkmm.examples.base");
MyWindow win;
return app->run(win);
}
This code use GTKmm 3.0 lib
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如您所猜测的,
Start()
方法会阻塞,因此其他 Gtk 代码都没有机会运行。这意味着什么都没有完成,甚至没有绘制 UI。相反,让
child
成为该类的成员。接下来,使用async_pipe
而不是阻塞管道流,这样您就不必阻塞来读取。现在,设置一个异步读取循环来响应来自子进程标准输出的传入数据。我创建了一个简单的 dotnet core 控制台应用程序来测试这一点:
现在我们将默认的 Program.cs 替换为:
构建并再次运行打印,总时间跨度为 5 秒:
执行 GTK 方面
我简化了很多事情。
最棘手的部分是从 Gtk 事件循环中轮询 io_context。我选择使用
g_add_timeout
来达到此目的。正确取消注册刻度处理程序非常重要,因此在破坏MyWindow
后不会产生未定义的行为。为了达到良好的效果,我添加了一个
Stop
按钮,并确保适当地启用/禁用Start
/Stop
按钮。让我们进行一些现场演示:完整演示
As you've guessed, the
Start()
method blocks, so no other Gtk code gets a chance to run. This means nothing gets done, not even drawing the UI.Instead, make the
child
a member of the class. Next, use anasync_pipe
instead of the blocking pipe stream, so you don't have to block to read either. Now, set-up an async read loop to respond to incoming data from the child process'es standard output.I've created a simple dotnet core console application to test this with:
Now we replace the default Program.cs with:
Building and running again prints, over a total timespan of 5 seconds:
Doing The GTK Side
I've simplified many things.
The trickiest part is to make the
io_context
be polled from the Gtk event loop. I opted to useg_add_timeout
for the purpose. It is very important to correctly de-register the tick handler, so no undefined behavior results afterMyWindow
is destructed.I added a
Stop
button for good measure, and made sure thatStart
/Stop
buttons are enabled/disabled as appropriate. Let's do some live demo:Full Demo