2017-09-22 136 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