인프런 커뮤니티 질문&답변

김재우님의 프로필 이미지

작성한 질문수

[입문] Qt 6 프로그래밍 1편

QT에서 리눅스 실행 파일 실행 방법 문의

22.04.27 15:30 작성

·

1K

1

안녕하십니까. 강사님
 
1부강의를 듣고 나름 간단한 프로그램을 만들려고 합니다.
리눅스로 이미 누군가 만들어놓은 소스파일을 이용하여 QT에서 (리눅스 환경) 에서 해당 소스를 포함하여 GUI 작업을 하려 합니다.
이때 해당 소스파일을 gcc나 다른 컴파일러로 빌드하여 아래와 같이 터미널에서 실행시키는 프로그램인데
>./test
Hello World!!
 
QT에서 해당 실행파일을 실행 시키고 그 결과로 출력되는 값을 가지고 QT의 linetext 위젯같은 곳에 출력하고자 합니다.
QT에서 리눅스 실행파일을 실행시키고 해당 결과를 얻어올 수 있는 방법은 어떻게 하면 되는지 문의드립니다.
 
강의내용과 다소 상관없는 기술을 여쭤봐서 죄송합니다. 꾸뻑

답변 1

0

김대진님의 프로필 이미지
김대진
지식공유자

2022. 04. 27. 16:19

안녕하세요. 김재우님

Qt 에서 제공하는 클래스 중에서 QProcess 클래스를 사용하면 외부프로그램을 실행하고 결과를 얻어올 수 있습니다. 

결과를 얻어올 때 2가지 방법이 있습니다. 동기화 방식이 있고 비 동기화 방식이 있어요.

동기화 방식은 외부 프로그램을 실행시키면 결과가 끝날때 까지 프로그램을 블록킹 시키고, 결과를 받으면 실행 하는 방법입니다. 이 방법은 아래와 같이 하시면 됩니다.

 

QProcess process;

process.start("sh");

process.write("/home/my/test");

process.closeWriteChannel( );

process.waitForFinished( );

QByteArray my_data = process.readAll();

process.close( )

 

이렇게 해주면 됩니다. 그리고 waitForFinished( ) 함수에 인자가 없으면 끝날까지 기다립니다. 하지만 응답이 없어도 끝내게 하기 위해서 인자를 사용할 수 있습니다. 예를 들어 3초 후 끝나게 하고 싶다면 아래와 같이 사용하시면 됩니다.

process.waitForFinished( 3000 );

 

그리고 비 동기화 방식은  결과를 받을때까지 기다리는 것이 아니라 Siganl 과 Slot 을 연결해 비동기 방식으로 결과를 받을 수 있어요. 아래와 같이 

connect(myProcess,  SIGNAL(readyReadStandardOutput()), this, SLOT(printMsg()));  

함수를 사용하면 됩니다. 결과를 받아오면 printMsg( ) 함수를 실행합니다.

QString program = QString("/home/my/test"); myProcess->start(program);

김재우님의 프로필 이미지
김재우
질문자

2022. 05. 03. 14:58

답변감사드립니다. 

비동기로 처리하는 방식관련하여 추가 질문 드려도 될까요?

비동기 방식이라 함은 외부프로세스가 실행될때 그 프로세스 실행이 끝났을때 시그널 이벤트가 발생하고

해당 슬롯함수를 호출하는 것으로 보입니다. 따라서 동기방식과 얻어오는 값은 같은것 같은데

외부 프로그램을 실행하고 그 출력을 한꺼번에 얻어오는 것이 아니라 그 프로그램이 실행중일때

output 되는 메시지들을 라인단위로, 실시간으로 가져올 수 있는 방법은 없는지 문의드립니다. 

예를들어 1초마다 hello world 1   일초후  hello world 2  일초후  hello world 3 이런식으로 

출력되는 외부 프로세스를 실행시킬때  각 라인이 출력될때마다 값을 가져오고 싶습니다. 

감사합니다.

김대진님의 프로필 이미지
김대진
지식공유자

2022. 05. 03. 15:08

안녕하세요. 재우님

QProcess 로 끝나고 얻어오는 것이 아니라 중간중간 결과를 가져오는 것이 라면 비 동기 방식으로 하면 가능합니다. 

김재우님의 프로필 이미지
김재우
질문자

2022. 05. 04. 10:03

강사님 실례를 무릅쓰고 한번더 질문드립니다. 

아래코드와 같이 hello를 1초마다 10번찍는 외부실행파일을 실행시켰습니다. 

비동기방식으로 처리하여 1초마다 QT의 textedit에서도 찍고 싶은데.

실행결과 10의 문자열이 한꺼번에 출력됩니다.  비동기방식으로 중간중간 결과를 가져오고 싶은데..

이떻게 수정해야 될지 문의 드립니다. 참고로 윈도우에서 테스트중입니다.

Widget::Widget(QWidget *parent)    : QWidget(parent)    , ui(new Ui::Widget)

{

   ui->setupUi(this);

   setWindowTitle("Netwrok Traffic Tester");

    box = new QMessageBox;

    connect(ui->pbt_Start, SIGNAL(pressed()),

            this, SLOT(slotPbtStart()));

    connect(ui->pbt_Stop, SIGNAL(pressed()),

            this, SLOT(slotPbtStop()));

    Process = new QProcess(this);

    connect( Process, SIGNAL(readyReadStandardOutput()), this, SLOT(slotPrintProcessMsg()));

}

void Widget::slotPbtStart(){

     QString _exePath = "C://hello.exe";

    QStringList _arguments;

    QByteArray rec_data;

    ui->textEdit->clear();

    Process->start(_exePath);

    Process->waitForStarted();

}

void Widget::slotPbtStop()

{

      Process->close();

     }

void Widget::slotPrintProcessMsg()

{

    QByteArray rec_data = Process->readAllStandardOutput();

    ui->textEdit->append(rec_data);

}

 

김대진님의 프로필 이미지
김대진
지식공유자

2022. 05. 04. 11:28

안녕하세요. 김재우님, 

slotPbtStart( ) 함수에서 Process->waitForStarted() 를 사용하면 동기식으로 받기 때문에 프로그램이 종료 될때 까지 기다렸다가 결과를 한번에 받아옵니다. 따라서 아래 예제와 같이 해보시면 어떨까 싶어서 ping 으로 실시간으로 결과를 받아오는 것을 예제로 만들어 보았습니다.

 

widget.h 

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QProcess>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    QProcess    *m_process;

private slots:
    void slot_pushButton();
    void slotPrintProcessMsg();

};
#endif // WIDGET_H

 

widget.cpp 

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QDateTime>

Widget::Widget(QWidget *parent)
    : QWidget(parent), ui(new Ui::Widget)
    , m_process(new QProcess)
{
    ui->setupUi(this);
    connect(ui->pushButton, SIGNAL(pressed()), this, SLOT(slot_pushButton()));

    connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(slotPrintProcessMsg()));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::slot_pushButton()
{
    qDebug() << Q_FUNC_INFO;

    QStringList arguments;
    arguments << "-t" << "168.126.63.1";
    m_process->start("ping", arguments);

}

void Widget::slotPrintProcessMsg()
{
    QByteArray outData = m_process->readAllStandardOutput().trimmed();
    QString currDt = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");

    qDebug("[%s] Received Message ---> [ %s ] ", currDt.toLocal8Bit().data(), outData.data() );
}


 

 

위와 같이 하시면 slotPrintProcessMsg( ) 함수 반복해 결과를 받아옵니다. 

혹시 더 궁금하신것 있으면 언제든 질문해주세요.

 

김재우님의 프로필 이미지
김재우
질문자

2022. 05. 04. 14:08

감사합니다.

올려주신 코드를 따라 ping 테스트로 값을 실시간으로 가져오는 것을 확인했습니다. 

하지만 제가 만든 hello world 실행파일이나, 제가 긍극적으로 하려고 하는 iperf3(네트워크 트래픽 테스트) 프로그램은 같은 방법을 사용하였지만 중간에 라인마다 값을 가져오지 않고 한꺼번에 가져 옵니다. 

ping과 iperf3 프로그램은 실행될때 standard output 방식이 달라서 ping 프로그램과는 달리 iperf3는 중간값이 출력될때 시그널을 발생시키지 못하는 것 같습니다. 

이 부분관련해서 조언을 주실 수 있으신지 미자막으로 문의드립니다. 

감사합니다.

 

김대진님의 프로필 이미지
김대진
지식공유자

2022. 05. 04. 14:29

그렇다면 start( ) 함수 호출하기 전에 

my_proc->setProcessChannelMode(QProcess::MergedChannels);

을 사용해보시거나 

my_proc->setProcessChannelMode(QProcess::ForwardedOutputChannel);

을 사용하시면 어떨까 싶습니다. 

 

그리고 iperf 에 --forceflush 라는 옵션을 사용해보심 어떨까 싶네요.

김재우님의 프로필 이미지
김재우
질문자

2022. 05. 18. 11:43

안녕하세요 강사님

결과가 궁금하실것 같아 말씀드립니다. 

알려주신대로 iperf에서 --forceflush 옵션을 쓰니 원하는대로 라인바이라인 으로 출력이 됩니다. 

다만 iperf3의 windows버전에서는 아직까지 --forceflush를 지원해 주지 않아 한참을 해맸습니다.

리눅스에서는 지원이 되어 가능은한데 window는 아직 성공하지 못했습니다. 

큰도움이 되었습니다. 

김대진님의 프로필 이미지
김대진
지식공유자

2022. 05. 18. 13:30

안녕하세요. 김재우님, 

해결이 되었다니 제가 더 기쁩니다. ^^ 

프로젝트 꼭 성공하세요. ~