2017-09-22 127 views
0

我正在創建一個程序,我在Qt中使用啓動了C++ 11的Ubuntu 16.04 Qt 5.5.1上的QProcess框架運行進程。我正在將流程輸出流引導到QTextEdit。在QProcess輸出中保留ANSI轉義序列

我想將此輸出着色爲使用本地終端使用嵌入式ANSI轉義顏色序列解釋的相同顏色。但是,我無法解析轉義序列,因爲它們看起來是從QProcess輸出中丟失的。我原本以爲QString正在剝離它們,但經過一些測試,我不認爲這是事實。

我發現some information指向ANSI逃逸顏色解釋方向,如果我可以只保留QProcess輸出中的轉義序列。

這是我在Qt代碼中做的一個示例項目。

源文件...

#include "mainwindow.h" 
#include "ui_mainwindow.h" 
#include <QString> 
#include <QProcess> 
#include <QStringList> 

MainWindow::MainWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::MainWindow) 
{ 
    ui->setupUi(this); 

    QStringList input = {"gcc will_not_build.c"}; 
    QProcess * proc = new QProcess(); 

    proc->setReadChannel(QProcess::StandardOutput); 
    proc->setProcessChannelMode(QProcess::MergedChannels); 
    proc->setWorkingDirectory("/path/to/test/c/file/"); 

    //Start bash 
    proc->start("bash"); 
    proc->waitForStarted(); 

    // Write as many commands to this process as needed 
    foreach(QString str, input){ 
     proc->write(str.toUtf8() + "\n"); 
     proc->waitForBytesWritten(-1); 
    } 

    // Let bash close gracefully 
    proc->write("exit $?\n"); 
    proc->waitForBytesWritten(-1); 

    proc->closeWriteChannel(); 
    proc->waitForFinished(); 
    proc->waitForReadyRead(); 

    QByteArray read_data = proc->readAll(); 

    // The use of tr(read_data) also works here. 
    QString output = tr(read_data);//QString::fromStdString (read_data.toStdString()); 

    proc->closeReadChannel(QProcess::StandardOutput); 

    proc->close(); 
    delete proc; 

    // Add the output to the text box 
    ui->textEdit->append (output); 
} 

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

頭文件...

#ifndef MAINWINDOW_H 
#define MAINWINDOW_H 

#include <QMainWindow> 

namespace Ui { 
class MainWindow; 
} 

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 

public: 
    explicit MainWindow(QWidget *parent = 0); 
    ~MainWindow(); 

private: 
    Ui::MainWindow *ui; 
}; 

#endif // MAINWINDOW_H 

形式文件...

<?xml version="1.0" encoding="UTF-8"?> 
<ui version="4.0"> 
<class>MainWindow</class> 
<widget class="QMainWindow" name="MainWindow"> 
    <property name="geometry"> 
    <rect> 
    <x>0</x> 
    <y>0</y> 
    <width>400</width> 
    <height>300</height> 
    </rect> 
    </property> 
    <property name="windowTitle"> 
    <string>MainWindow</string> 
    </property> 
    <widget class="QWidget" name="centralWidget"> 
    <widget class="QTextEdit" name="textEdit"> 
    <property name="geometry"> 
    <rect> 
     <x>33</x> 
     <y>19</y> 
     <width>331</width> 
     <height>211</height> 
    </rect> 
    </property> 
    </widget> 
    </widget> 
    <widget class="QMenuBar" name="menuBar"> 
    <property name="geometry"> 
    <rect> 
    <x>0</x> 
    <y>0</y> 
    <width>400</width> 
    <height>19</height> 
    </rect> 
    </property> 
    </widget> 
    <widget class="QToolBar" name="mainToolBar"> 
    <attribute name="toolBarArea"> 
    <enum>TopToolBarArea</enum> 
    </attribute> 
    <attribute name="toolBarBreak"> 
    <bool>false</bool> 
    </attribute> 
    </widget> 
    <widget class="QStatusBar" name="statusBar"/> 
</widget> 
<layoutdefault spacing="6" margin="11"/> 
<resources/> 
<connections/> 
</ui> 

的C源文件...

int main(){ 
    // Intentionally will not build 
    I will not build :) 
} 

我的輸出是這樣的:

QProcess中gcc的輸出

本地Linux終端的輸出是這樣的:顏色

Linux終端gcc的輸出

有沒有人知道如何去保持QProcess輸出中的ANSI轉義顏色序列,以便我可以模擬Linux終端顏色?

作爲我在Qt Creator源代碼中挖掘出來的一個附註,有一個類可以將ANSI轉義顏色轉換爲Rich Text顏色,因此我知道有人已經走過了這條路。然後再一次,在構建項目時,由於某種原因,Qt Creator未將自己的終端中的構建輸出着色。

回答

0

QProcess不與過程輸出干擾,它只是gcc - 發射彩色輸出很多其他程序 - 默認情況下發出的顏色轉義序列只有當它檢測到它寫一個TTY設備上。

如果要禁用此啓發式並要求始終生成彩色輸出,則必須將-fdiagnostics-color=always選項添加到編譯器命令行。

+0

謝謝您的回答!這正好解釋了我的結果。 Gcc是我將運行的許多程序之一,所以命令行選項可能並不總是有效。快速谷歌顯示它可能欺騙程序認爲他們正在寫入TTY設備。 https://superuser.com/questions/352697/preserve-colors-while-piping-to-tee – konakid

+0

@konakid:是的,這是一個解決方案,但請記住,VT100轉義序列不僅僅包含顏色,而且包含方式你可能不得不大量過濾來自「更聰明」程序的輸出。 –

1

由於我的問題非常有見地的答案,我能夠找到解決我的問題。我會分享...

QProcess沒有錯,也不是QString。問題在於程序執行的環境。由於這些程序(gcc etc)的輸出沒有連接到TTY設備,因此所有ANSI轉義序列都被剝離。雖然有一種方法to trick the output to appear as if it were connected to a TTY device

只需在命令前加上unbuffer即可。

由於我的使用實際上是創建一個Qt Creator插件,我已經連接了大部分Qt Creator源代碼。只是發生了一個名爲AnsiEscapeCodeHandler的便利類,它已經存在將ANSI轉義序列轉換爲QTextCharFormat's和相應的ANSI轉義序列剝離字符串。

爲了說明我是如何使用這個類的,現在在我的例子中,我將從可下載的Qt Creator源代碼中複製ansieescapecodehandler.hansiescapecodehandler.cpp到我的測試項目。我不得不從AnsiEscapeCodeHandler源文件中刪除幾行來編譯Qt Creator源代碼的其他部分,但就是這樣。

新源文件...

#include "mainwindow.h" 
#include "ui_mainwindow.h" 
#include <QString> 
#include <QProcess> 
#include <QStringList> 

MainWindow::MainWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::MainWindow) 
{ 
    ui->setupUi(this); 

    QStringList input = {"unbuffer gcc will_not_build.c"}; 
    QProcess * proc = new QProcess(); 

    proc->setReadChannel(QProcess::StandardOutput); 
    proc->setProcessChannelMode(QProcess::MergedChannels); 
    proc->setWorkingDirectory("/path/to/test/c/file/"); 

    //Start bash 
    proc->start("bash"); 
    proc->waitForStarted(); 

    // Write as many commands to this process as needed 
    foreach(QString str, input){ 
     proc->write(str.toUtf8() + "\n"); 
     proc->waitForBytesWritten(-1); 
    } 

    // Let bash close gracefully 
    proc->write("exit $?\n"); 
    proc->waitForBytesWritten(-1); 

    proc->closeWriteChannel(); 
    proc->waitForFinished(); 
    proc->waitForReadyRead(); 

    QByteArray read_data = proc->readAll(); 

    // The use of tr(read_data) also works here. 
    QString output = tr(read_data);//QString::fromStdString (read_data.toStdString()); 

    proc->closeReadChannel(QProcess::StandardOutput); 

    proc->close(); 
    delete proc; 

    // Strip default character set escape sequences, since those seem to be left 
    // See https://stackoverflow.com/questions/36279015/what-does-x1bb-do 
    output.remove("\x1b(B", Qt::CaseInsensitive); 

    // Since it is just one single text stream define here instead of globally 
    Utils::AnsiEscapeCodeHandler ansi_handler; 

    FormattedTextList result = ansi_handler.parseText (Utils::FormattedText(output, ui->textEdit->currentCharFormat())); 

    // Loop through the text/format results 
    foreach(Utils::FormattedText ft, result){ 
     ui->textEdit->setCurrentCharFormat (ft.format); 
     ui->textEdit->insertPlainText (ft.text); 
    } 
} 

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

新的頭文件...

#ifndef MAINWINDOW_H 
#define MAINWINDOW_H 

#include <QMainWindow> 

// This exists in the qtcreator-src code and handles ansi escape code color parsing 
#include "ansiescapecodehandler.h" 

namespace Ui { 
class MainWindow; 
} 

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 

public: 
    explicit MainWindow(QWidget *parent = 0); 
    ~MainWindow(); 

private: 
    Ui::MainWindow *ui; 

    typedef QList<Utils::FormattedText> FormattedTextList; 
}; 

#endif // MAINWINDOW_H 

新的彩色輸出... QProcess gcc output