0

我正在使用 Qt 和 QProcess 从其他工具读取一些数据并将它们打印在我的应用程序上。例如,把它想象成一个“终端”。

我正在使用 QProcess::canReadLine() 和 QProcess:readLine() 处理数据,这很棒。但是有些工具使用 \r 在屏幕上打印进度条,这与我的解析器有关。由于从来没有要读取的行,我的应用程序只是等到进程完成打印最后一行:许多行用 \r 而不是 \n 粘合在一起。

无论如何,有没有办法告诉 QProcess 也使用 \r 作为换行符?我想实现我的 QIODevice 子类,但我也需要重新实现 QProcess,所以这似乎不是最佳方法。

我想使用一个中间缓冲区,并使用这个缓冲区向我的主程序发送“hasLine”信号。我会使用 QProcess::readyRead 来填充缓冲区,然后使用缓冲区来填充我的主应用程序,但我只想告诉 Qt \r 作为换行符也可以。那可能吗?

4

2 回答 2

2

我认为不可能直接告诉 Qt 使用 '\r' 作为换行符。我认为 QTextStream 可以做到这一点,但现在看看它的来源,在我看来我错了。

一种有趣的方法是实现一个自定义的 QIODevice 子类,该子类从另一个 QIODevice 读取并只是将所有 '\r' 替换为 '\n',将除 read() 变体之外的所有其他方法委托给原始设备。然后 readLine() 和 QTextStream 将在结果流上正常工作,我认为。不过,您必须以某种方式处理可能的 '\r\n' 序列。好处是您不必在该类中进行任何缓冲。

这些方面的东西:

class CRFilter: public QIODevice {
    Q_OBJECT
public:
    CRFilter(QIODevice *device);
protected:
    virtual qint64 readData(char *data, qint64 maxSize);
    virtual qint64 writeData(const char *data, qint64 maxSize);
private:
    QIODevice *device;
};

CRFilter::CRFilter(QIODevice *device):
device(device)
{
    // delegate the readyRead() signal to this object
    connect(device, SIGNAL(readyRead()), SIGNAL(readyRead()));
    // and maybe other signals like bytesWritten() too...
}

qint64 CRFilter::readData(char *data, qint64 maxSize)
{
    qint64 res = device->read(data, maxSize);
    for (qint64 i = 0; i < res; i++) {
        if (data[i] == '\r')
            data[i] = '\n';
    }
    return res;
}

qint64 CRFilter::writeData(const char *data, qint64 maxSize)
{
    return device->write(data, maxSize);
}

然后你就这样做:

QProcess process; // use QProcess methods on this
CRFilter reader(&p); // use QIODevice methods on this
reader.open(QIODevice::ReadWrite); // need this to convince read()/write() methods to work

我没有实际测试过它,所以它可能需要一些调试才能让它正确。我也认为这有点难看,但想不出任何真正优雅的解决方案。

于 2011-12-18T07:32:55.110 回答
1

由于我没有对此使用多态性,因此公开继承并覆盖一些方法和信号没有问题:

QCLIProcess.h

#ifndef QCLIPROCESS_H
#define QCLIPROCESS_H

#include <QProcess>

class QCLIProcess : public QProcess
{
    Q_OBJECT

public:
    explicit QCLIProcess(QObject *parent = 0);
    bool canReadLine() const;
    QString readLine();

signals:
    void readyRead();

private slots:
    void processLine();

private:
    QByteArray buffer;
    QStringList lines;
};

#endif // QCLIPROCESS_H

QCLIProcess.cpp

#include "QCLIProcess.h"
#include <QtCore>

QCLIProcess::QCLIProcess(QObject *parent) :
    QProcess(parent)
{
    setReadChannelMode(QProcess::MergedChannels);
    connect((QProcess *)this, SIGNAL(readyRead()), this, SLOT(processLine()));
}

void QCLIProcess::processLine(){
    buffer.append(readAll());

    int last = 0;
    for(int i=0; i<buffer.size(); i++){
        if (buffer.at(i) == '\n' || buffer.at(i) == '\r'){
            QString line(buffer.mid(last, i-last));
            line.append('\n');
            if (!line.isEmpty()) lines << line;
            last = i+1;
        }
    }
    buffer.remove(0, last);
    emit readyRead();
}

bool QCLIProcess::canReadLine() const {
    return !lines.isEmpty();
}

QString QCLIProcess::readLine(){
    QString line;

    if (!lines.isEmpty()){
        line = lines.at(0);
        lines.removeFirst();
    }

    return line;
}

更新: 我结束了将 QProcess 封装在一个新类中,而不是派生它。通过这种方式,我可以控制要公开哪些信号和哪些插槽。

QLineBufferedCRFilteredProcess.h #ifndef QCLIPROCESS_H #define QCLIPROCESS_H

#include <QProcess>

class QLineBufferedCRFilteredProcess : public QObject
{
    Q_OBJECT

public:
    explicit QLineBufferedCRFilteredProcess(QObject *parent = 0);
    bool canReadLine() const;
    QString readLine();

    void start(const QString &program, const QStringList &arguments);
    void close();

signals:
    void readyRead();
    void finished(int exitCode, QProcess::ExitStatus exitStatus);

private slots:
    void processLine();

private:
    QProcess process;
    QByteArray buffer;
    QStringList lines;
};

#endif // QCLIPROCESS_H

QLineBufferedCRFilteredProcess.cpp #include "QLineBufferedCRFilteredProcess.h" #include

QLineBufferedCRFilteredProcess::QLineBufferedCRFilteredProcess(QObject *parent) :
    QObject(parent)
{
    process.setReadChannelMode(QProcess::MergedChannels);
    connect(&process, SIGNAL(readyRead()), SLOT(processLine()));
    connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus)));
}

void QLineBufferedCRFilteredProcess::processLine()
{
    buffer.append(process.readAll());

    int last = 0;
    for(int i=0; i<buffer.size(); i++){
        if (buffer.at(i) == '\n' || buffer.at(i) == '\r'){
            QString line(buffer.mid(last, i-last));
            line.append('\n');
            if (!line.isEmpty()) lines << line;
            last = i+1;
        }
    }
    buffer.remove(0, last);
    emit readyRead();
}

bool QLineBufferedCRFilteredProcess::canReadLine() const
{
    return !lines.isEmpty();
}

QString QLineBufferedCRFilteredProcess::readLine()
{
    QString line;

    if (!lines.isEmpty()){
        line = lines.at(0);
        lines.removeFirst();
    }

    return line;
}

void QLineBufferedCRFilteredProcess::start(const QString &program, const QStringList &arguments)
{
    process.start(program, arguments);
}

void QLineBufferedCRFilteredProcess::close()
{
    process.close();
}
于 2011-12-18T23:16:21.303 回答