Creating QT Application as GUI for existing console-based application on windows

前端 未结 2 2068
日久生厌
日久生厌 2020-12-06 15:25

i\'m trying to set up a GUI with Qt for an existing application which is meant to be run in the windows commandline. It\'s not just running the app with the system()<

相关标签:
2条回答
  • 2020-12-06 16:18

    I found a solution for my needs and can do what i want to do.. Actually i'm a bit disappointed. I thought it would be something more complex.

    First i have to say it's an QtQuick Application .. Maybe i should have said that earlier.

    I simply outsourced the process functions to another class. This class is passed to QML via the qmlRegisterType<>() function. I connected some signals from the process ( QProcess ) to slots in my own class and wrote my own functions to handle reading/writing data from and to the console application. With the QML-onClicked events i can pass my parameters and strings to the console app. And with some application logic i can handle the in/out requests and timings.

    WrapperClass.h

    class WrapperClass: public QObject
    {
        Q_OBJECT
    
    public:
        explicit WrapperClass(QObject *parent = nullptr);
    
        QProcess *process;
        QString str_proc_output;
    
        Q_INVOKABLE void startProcess();
        Q_INVOKABLE void stopProcess();
    
        Q_INVOKABLE QString getOutput();
        Q_INVOKABLE void writeByte(QString str);
    
    
        Q_INVOKABLE QString getAllOutput();
    private:
    
    signals:
    
    public slots:
        void mReadyRead();
        void mReadyReadStandardOutput();
        void mFinished(int code);
        void mBytesWritten(qint64 written);
    
    };
    

    WrapperClass.cpp

    WrapperClass::WrapperClass(QObject *parent) : QObject(parent)
    {
        process = new QProcess();
        process->setProgram("untitled.exe");
        process->setProcessChannelMode(QProcess::MergedChannels);
    
        str_proc_output = "";
    
        connect(process, SIGNAL(readyRead()), this, SLOT(mReadyRead()));
        connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(mReadyReadStandardOutput()));
        connect(process, SIGNAL(finished(int)), this, SLOT(mFinished(int)));
        connect(process, SIGNAL(bytesWritten(qint64)), this, SLOT(mBytesWritten(qint64)));
    
    }
    
    void WrapperClass::startProcess() {
        if(process->state() == QProcess::Running) {
            stopProcess();
        } else {
            process->open(QProcess::ReadWrite);
        }
    }
    
    void WrapperClass::stopProcess() {
        process->close();
    }
    
    
    
    QString WrapperClass::getOutput() {
        return str_proc_output;
    }
    
    
    QString WrapperClass::getAllOutput() {
        QString str = process->readAll();
    
        std::cout << str.toStdString() << std::endl;
        return str;
    }
    
    
    void WrapperClass::writeByte(QString str) {
    
        char cArr[str.length()] = {};
    
        memcpy(cArr, str.toStdString().c_str(), str.length());
    
        QByteArray arr = QByteArray(cArr, -1);
        process->write(arr);
    }
    
    
    
    
    void WrapperClass::mReadyRead() {
        QString s = QString(process->readAll());
    
        std::cout << "ReadyRead: " << s.toStdString() << std::endl;
        str_proc_output = s;
    }
    
    void WrapperClass::mReadyReadStandardOutput() {
        QString s = QString(process->readAllStandardOutput());
    
        std::cout << "ReadyReadStandardOutput: " << s.toStdString() << std::endl;
    
    }
    
    void WrapperClass::mFinished(int code) {
        std::cout << "Process finished! (" << code << ')' << std::endl;
    }
    
    
    void WrapperClass::mBytesWritten(qint64 written) {
    
        std::cout << "Bytes written: " << written << std::endl;
    
    }
    

    Main.cpp

    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        qmlRegisterType<WrapperClass>("com.example.WrapperClass", 0, 1, "WrapperClass");
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    }
    

    With registering the Cpp-Class to QML i'm able to trigger the read/write functions via Click-Events from QML-MouseArea or Button.

    0 讨论(0)
  • 2020-12-06 16:22

    Out of curiosity, I played around with QProcess.

    I'm really impressed how easy and straight forward everything works (remembering with horror how difficult it was when we did it in the past without Qt).

    Thus, I can provide my little rather MCVE for demonstration.

    First I made a simple console application testQProcessIOChild.cc:

    #include <chrono>
    #include <iostream>
    #include <sstream>
    #include <string>
    #include <thread>
    
    using namespace std;
    
    // inspired by:
    // https://try.kotlinlang.org/#/Examples/Longer%20examples/99%20Bottles%20of%20Beer/99%20Bottles%20of%20Beer.kt
    string bottlesOfBeer(int n)
    {
      switch (n) {
        case 0: return "no more bottles of beer";
        case 1: return "1 bottle of beer";
        default: {
          ostringstream outFmt;
          outFmt << n << "  bottles of beer";
          return outFmt.str();
        }
      }
    }
    
    int main()
    {
      enum { delay = 1000 };
      for (int n;;) {
        cout << "Initial number of bottles (-1 ... finish): " << flush;
        if (!(cin >> n)) {
          cerr << "Input error!" << endl;
          continue;
        }
        if (n < -1) {
          cerr << "Illegal input!" << endl;
          continue;
        }
        if (n < 0) break;
        if (n > 100) {
          cerr << "The ministry of health warns:" << endl
            << " Abuse of alcoholics may damage your health." << endl;
          n = 99;
        }
        cout << "Go to the store and buy some more, "
          << bottlesOfBeer(n) << " on the wall." << endl;
        while (n) {
          this_thread::sleep_for(chrono::milliseconds(delay));
          cout << bottlesOfBeer(n) << " on the wall, "
            << bottlesOfBeer(n) << '.' << endl
            << "Take one down, pass it around, ";
          --n;
          cout << bottlesOfBeer(n) << " on the wall." << endl;
        }
        this_thread::sleep_for(chrono::milliseconds(delay));
        cout << "No more bottles of beer on the wall, no more bottles of beer."
          << endl;
      }
      return 0;
    }
    

    I did this to have something which provides:

    • usage of standard input
    • usage of standard output
    • usage of standard error
    • certain specific time consuming behavior (to see whether and when the parent process reacts).

    Second I made the Qt GUI application testQProcessIO.cc as wrapper:

    // Qt header:
    #include <QtWidgets>
    
    const char *childProgram = "./testQProcessIOChild";
    
    int main(int argc, char **argv)
    {
      qDebug() << QT_VERSION_STR;
      // main application
      QApplication app(argc, argv);
      QProcess qProcessChild;
      // GUI setup
      QWidget qWin;
      QGridLayout qGrid;
      QPushButton qBtnStart(QString::fromUtf8("Start"));
      qGrid.addWidget(&qBtnStart, 0, 0);
      QPushButton qBtnStop(QString::fromUtf8("Stop"));
      qBtnStop.setEnabled(false);
      qGrid.addWidget(&qBtnStop, 0, 1);
      QLabel qLblInput(QString::fromUtf8("Input: "));
      qLblInput.setEnabled(false);
      qGrid.addWidget(&qLblInput, 0, 2);
      QLineEdit qInput;
      qInput.setEnabled(false);
      qGrid.addWidget(&qInput, 0, 3);
      QTextEdit qTxtLog;
      qTxtLog.setReadOnly(true);
      qGrid.addWidget(&qTxtLog, 1, 0, 1, 4);
      qGrid.setRowStretch(1, 1);
      qGrid.setColumnStretch(3, 1);
      qWin.setLayout(&qGrid);
      qWin.show();
      // install signal handlers
      QObject::connect(&qBtnStart, &QPushButton::clicked,
        [&](bool) {
          qProcessChild.start(QString::fromLatin1(childProgram));
        });
      QObject::connect(&qBtnStop, &QPushButton::clicked,
        [&](bool) {
          qProcessChild.kill();
        });
      QObject::connect(&qInput, &QLineEdit::returnPressed,
        [&](){
          QString text = qInput.text() + '\n';
          qProcessChild.write(text.toLatin1());
        });
      QObject::connect(&qProcessChild, &QProcess::started,
        [&]() {
          qBtnStart.setEnabled(false);
          qBtnStop.setEnabled(true);
          qLblInput.setEnabled(true);
          qInput.setEnabled(true);
        });
      QObject::connect(&qProcessChild,
        // cast needed because QProcess::finished() is polymorph
        (void(QProcess::*)(int))&QProcess::finished,
        [&](int) {
          qBtnStart.setEnabled(true);
          qBtnStop.setEnabled(false);
          qLblInput.setEnabled(false);
          qInput.setEnabled(false);
          qTxtLog.clear();
        });
      QObject::connect(&qProcessChild, &QProcess::readyReadStandardOutput,
        [&]() {
          qTxtLog.append(qProcessChild.readAllStandardOutput());
        });
      QObject::connect(&qProcessChild, &QProcess::readyReadStandardError,
        [&]() {
          qTxtLog.append(qProcessChild.readAllStandardError());
        });
      // run application
      return app.exec();
    }
    

    I compiled and tested this with VS2013 and Qt 5.9.2 on Windows 10 (64 bit).

    To illustrate the test session I wrote a QMake project afterwards testQProcessIO.pro:

    SOURCES = testQProcessIO.cc
    
    QT += widgets
    

    and compiled and tested on cygwin again:

    $ g++ -std=c++11 -o testQProcessIOChild testQProcessIOChild.cc 
    
    $ ./testQProcessIOChild 
    Initial number of bottles (-1 ... finish): -1
    
    $ qmake-qt5 testQProcessIO.pro
    
    $ make
    g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQProcessIO.o testQProcessIO.cc
    g++  -o testQProcessIO.exe testQProcessIO.o   -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread 
    
    $ ./testQProcessIO
    5.9.2
    
    $
    

    Snapshots of my test sessions:

      

    0 讨论(0)
提交回复
热议问题