- QProcess 란?

QProcess 클래스는 프로그램 내부에서 명령어나 다른 실행파일 등을 실행 하기 위해 사용합니다. 

로컬 환경에 있는 다른 프로그램을 실행시키거나 ssh 접속을 통해 다른 시스템의 프로그램, 쉘 스크립트, 쉘 명령 등을 실행할 수 있습니다.

 

- strat() 함수: 외부 프로그램 실행

현재 실행 중인 프로그램에서 start() 함수를 통해 외부 프로그램을 실행시킵니다.

QProcess process;
process.start("stdbuf -oL [프로그램 절대경로]");
process.waitForStarted(); // 또는 process.waitForFinished();

 

process.start("[프로그램 절대경로]")의 형식으로 실행시켜도 되지만 출력 버퍼 쪽에 버퍼링이 생깁니다.

입출력 버퍼에 데이터를 저장하는 행동을 버퍼링(buffering) 이라고 부릅니다.

 

이 때, stdbuf를 통해 버퍼링을 제한하여 pipe의 buffer를 강제로 비워 외부 프로그램의 출력 값을 바로 가져올 수 있습니다.

실행한 외부 프로그램의 출력 값을 현재 프로그램으로 받아와 실시간 처리 가 가능하기 때문에 위와 같이 사용하였습니다.

 

- start() 함수 실행 후 프로그램 동작

아래의 두 함수를 통해 외부 프로그램 실행 후 현재 프로그램의 다음 동작을 제어할 수 있습니다.

process.waitForStarted();   // 외부 프로그램 실행 후 바로 현재 프로그램의 이후 코드 진행
process.waitForFinished(); // 외부 프로그램 종료 시까지 waitForFinished() 함수에서 블로킹


외부 프로그램을 현재 프로그램과 병렬적으로 실행 하고 싶다면 waitForStarted() 함수를 사용할 수 있고,
외부 프로그램의 종료된 후의 상태를 바탕으로 이후 현재 프로그램 동작을 제어 하고 싶은 경우에 waitForFinished() 함수를 사용합니다.

 

이때, 외부 프로그램이 멈춰 버리면 현재 프로그램도 waitForFinished() 실행 후 블로킹 이 되어있기 때문에 주의해야 합니다.

현재 프로그램이 계속해서 멈춰있는 상태를 막기 위해 Qt에서는 waitForFinished() 함수의 default 값이 30초로 설정되어 있습니다.

waitForFinished() 함수를 사용한 경우 외부 프로그램 종료 시까지 현재 프로그램은 블로킹 되어 있기 때문에 외부 프로그램의 종료를

알 수 있지만, waitForStarted() 함수 사용 시 외부 프로그램 실행 후 블로킹이 되지 않으므로 외부 프로그램의 종료를 알기 위해선 추가적인

signal/slot이 필요합니다.

외부 프로그램 종료 시 발생하는 signal은 finished(int, QProcess::ExitStatus) 를 사용하고 slot은 직접 구현해 주도록 합니다.
signal/slot은 다음과 같이 연결해줍니다.

QObject::connect(process,  SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(ProcessFinished(int, QProcess::ExitStatus)))


finished(int, QProcess::ExitStatus) 함수 의 매개변수는 아래와 같습니다. 

첫 번째 매개변수 는  SIGINT, SIGKILL, SIGSEGV, ... 등 발생한 signal 종류에 대한 번호 입니다.
두 번째 매개변수 는 외부 프로그램이 정상 종료일 경우 0, 비정상 종료일 경우 1의 종료 시의 상태 값 을 가집니다.

비정상 종료라 하면 kill 명령어에 의해 종료되었을 경우나 잘못된 메모리를 참조하여 Segmentation Error 가 발생하였을 때를 말합니다.

예제에서 slot은 아래와 같이 임의로 구현해 보았습니다.
외부 프로그램이 비정상 종료되었을 경우 define한 5개의 signal에 대한 경우를 textBrowser에 출력하는 코드입니다.

#define SIGINT               2
#define SIGKILL              9
#define SIGSEGV         11
#define SIGTERM         15
#define SIGTSTP           20
...
void MainWindow::ProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
    if(exitStatus == QProcess::CrashExit) // 비정상 종료의 경우
    {
        QString errorMsg = "";
        if(exitCode == SIGINT)
            errorMsg = "[CTRL] + [c] (SIGINT)\n";

        else if(exitCode == SIGKILL)
            errorMsg = "Process killed (SIGKILL)\n";

        else if(exitCode == SIGSEGV)
            errorMsg = "Segmentation fault (SIGSEGV)\n";

        else if(exitCode == SIGTERM)
            errorMsg = "Process killed (SIGTERM)\n";

        else if(exitCode == SIGTSTP)
            errorMsg = "[CTRL] + [z] (SIGTSTP)\n";;

        textBrowser->insertPlainText(errorMsg);
    }
}

 

- 프로그램 실행 결과

 

- 전체 소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
 104 105

// ---------- mainwindow.h ----------
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QProcess>
#include <QTextBrowser>
 
#define SIGINT              2
#define SIGKILL             9
#define SIGSEGV             11
#define SIGTERM             15
#define SIGTSTP             20
 
namespace Ui {
class MainWindow;
}
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = 0);
    void ExecuteProgram();
    ~MainWindow();
 
private slots:
    void ProcessFinished(int, QProcess::ExitStatus);
 
private:
    Ui::MainWindow *ui;
    QProcess *process;
    QTextBrowser *textBrowser;
};
 
#endif // MAINWINDOW_H
 
 
// ---------- mainwindow.cpp ----------
#include "mainwindow.h"
#include "ui_mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    process     = new QProcess(this);
    textBrowser = new QTextBrowser(this);
    textBrowser->setGeometry(1040380220);
    ExecuteProgram();
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::ExecuteProgram()
{
    QString program = QString("/home/build-program/program");
    QObject::connect(process,  SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(ProcessFinished(int, QProcess::ExitStatus)));
    process->start(program);
 
    if(process->waitForStarted() == false)
    {
        textBrowser->insertPlainText("External Program Starting Error\n");
    }
    else
    {
        textBrowser->insertPlainText("External Program Started\n");
    }
}
 
void MainWindow::ProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
    if(exitStatus == QProcess::CrashExit)
    {
        QString errorMsg = "";
 
        if(exitCode == SIGINT)
        {
            errorMsg = "[CTRL] + [c] (SIGINT)\n";
        }
        else if(exitCode == SIGKILL)
        {
            errorMsg = "Process killed (SIGKILL)\n";
        }
        else if(exitCode == SIGSEGV)
        {
            errorMsg = "Segmentation fault (SIGSEGV)\n";
        }
        else if(exitCode == SIGTERM)
        {
            errorMsg = "Process killed (SIGTERM)\n";
        }
        else if(exitCode == SIGTSTP)
        {
            errorMsg = "[CTRL] + [z] (SIGTSTP)\n";
        }
 
        textBrowser->insertPlainText(errorMsg);
    }
}
cs

 

'Qt' 카테고리의 다른 글

QString Class 정리  (0) 2020.11.26
QSplitter 클래스로 Widget 크기 조절하기  (0) 2020.11.04
Qt Designer 에서 styleSheet 사용하기  (0) 2020.10.23
QTableView에 ComboBox 삽입 및 고정하기  (0) 2020.10.19
Qt Remote Debugging  (0) 2020.06.12