QT与ros2的如何通过ui控制后台运行python节点

在ROS2中,一般都需要通过 ros2 run 指令或 ros2 launch指令去完成节点的运行,那么我可不可以通过ui界面去控制节点的开始与关闭呢?

上一章节讲了QT多线程应用与ROS2的topic通讯,实现了后台线程运行,以及按键控制其运行的操作。

那么这一章我想通过QT的ui界面去完成python节点与C++节点的通讯以及启动:

开始之前我需要先说明两个在linux中的常用指令:

第一行是用来查找linux正在运行的线程
ps aux | grep <线程的名字>
第二行是用来杀死正在运行的线程,-f 参数告诉 pkill 根据完整的命令行来匹配进程,而不是匹配进程名。
pkill -f  <线程的名字>

使用进程名匹配 (-n):
pkill -n "<进程名>"
-n 选项允许你通过进程名来匹配和杀死进程。

使用进程名匹配 (-n):
pkill -n "<进程名>"
-n 选项允许你通过进程名来匹配和杀死进程。

使用用户匹配 (-u):
pkill -u "<用户名>"
使用 -u 选项可以杀死指定用户的所有进程。

使用进程组ID匹配 (-g):
pkill -g "<进程组ID>"
-g 选项允许你通过进程组ID来杀死进程组中的所有进程。

使用进程ID匹配 (-p):
pkill -p "<进程ID>"
使用 -p 选项可以直接通过进程ID来杀死进程。

组合使用多个选项:
你可以组合使用多个选项来进一步细化匹配条件。例如,如果你只想杀死属于特定用户并且具有特定进程名的进程,可以这样做:
pkill -u <用户名> -n <进程名>

使用正则表达式 (-e):
pkill -e "<正则表达式>"
-e 选项允许你使用扩展的正则表达式来匹配进程名。

使用信号 (-<signal>):
默认情况下,pkill 使用 SIGTERM 信号来终止进程。你可以使用 -<signal> 选项来指定其他信号,例如 -9 表示 SIGKILL:
pkill -9 -f "<完整的命令行>"

第一步先创建了一个CommandExecutor.h类,该类可以在后台进行,通过对后台的命令符号进行处理,便可以直接对终端可操作的内容进行操作。

#ifndef COMMANDEXECUTOR_H
#define COMMANDEXECUTOR_H

#include <QObject>
#include <QThread>
#include <QProcess>

class CommandExecutor : public QObject
{
    Q_OBJECT

public:
    explicit CommandExecutor(QObject *parent = nullptr);
    ~CommandExecutor();

public slots:
    void executeCommand(const QString &command);
    void stopCommand();

signals:
    void commandOutput(const QString &output);
    void commandError(const QString &error);

private slots:
    void readStandardOutput();
    void readStandardError();
    void processFinished(int exitCode, QProcess::ExitStatus exitStatus);

private:
    QProcess *process;
    QThread workerThread;
};

#endif // COMMANDEXECUTOR_H

CommandExecutor.cpp文件

readStandardError:但是这种,只能传达这种消息,Output不可以传出这种消息

#include "commandexecutor.h"
#include <QProcess>
#include <QDebug>
CommandExecutor::CommandExecutor(QObject *parent) : QObject(parent), process(new QProcess)
{
    // Move this object to the worker thread
     // 注册QProcess::ExitStatus类型,注册为一个可使用的元类型(meta-type)
    qRegisterMetaType<QProcess::ExitStatus>();
    this->moveToThread(&workerThread);


    // Connect QProcess signals to slots
    connect(process, &QProcess::readyReadStandardOutput, this, &CommandExecutor::readStandardOutput);
    connect(process, &QProcess::readyReadStandardError, this, &CommandExecutor::readStandardError);
    connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &CommandExecutor::processFinished);
}

CommandExecutor::~CommandExecutor()
{
    stopCommand();
    workerThread.quit();
    workerThread.wait();
    delete process;
}

void CommandExecutor::executeCommand(const QString &command)
{
     workerThread.start();
    if (process->state() != QProcess::NotRunning) {
        qWarning() << "Process is already running. Please stop it first.";
        return;
    }
    QStringList arguments = command.split(" ");
    QString program = arguments.takeFirst();
    process->start(program, arguments);
}

void CommandExecutor::stopCommand()
{
   // if (process->state() != QProcess::NotRunning) {
        process->terminate();
      //  if (!process->waitForFinished(3000)) {
            process->kill();
            process->waitForFinished();
       //}
   // }
}

void CommandExecutor::readStandardOutput()
{
    QByteArray output = process->readAllStandardOutput();
    emit commandOutput(QString::fromLocal8Bit(output));
}

void CommandExecutor::readStandardError()
{
    QByteArray error = process->readAllStandardError();
    emit commandError(QString::fromLocal8Bit(error));
}

void CommandExecutor::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
    QString result = QString("Process finished with exit code %1, exit status %2")
                         .arg(exitCode)
                         .arg(exitStatus == QProcess::NormalExit ? "NormalExit" : "CrashExit");
    emit commandOutput(result);
}

在main函数中,需要创建一下,然后再去执行:

CommandExecutor executor;

executor.executeCommand("ros2 run village_li li4_node");

connect(ui->pushButton_1, &QPushButton::clicked,this,[&](){rosWorker->stop();executor.stopCommand();executor.executeCommand("pkill -f li4_node");});

但是在主页面回调函数,需要对原来的节点进行关闭,切记一定要sleep(1),这样有时间可以去关闭,如果没有他会在后台一直执行。

MainWindow::~MainWindow()
{
    executor.stopCommand();
    executor.executeCommand("pkill -f _node");
    QThread::sleep(1);
    rosWorker->stop();
    rclcpp::shutdown(); // 确保在退出时关闭ROS 2上下文
    delete ui;
}

点击打开节点:

点击关闭节点(1):

点击x号关闭(2):同时也会这样关闭这些信号(但是要记得加延时)

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值