Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

run a sh shell while clicking button in QML with QT5.15.0



  • Hi all
    I have google a lot about how to run a shell while clicking button in qml but those topics are too old such like 2013 or 2014 with qquick 1.0. The codes in main.cpp are totally different with those and header files as well.
    Could anyone give me a new demo code about how to run shell?
    Thanks


  • Moderators

    What do you mean by "run shell"? What are you trying to achieve?

    If you want to run an external process, use QProcess. If you want to show a terminal inside your app, it will be harder. You can probably use KDE's frameworks to get a Konsole inside your app, like Dolphin, Yakuake and others do.



  • Hi, I just want to run an external process which is just a binary file. I write a shell script to run that binary file and want to run this shell while clicking the button in QML.
    Do you have any demo codes?


  • Lifetime Qt Champion



  • @mrjj Hi, I followed your post but an error occured.
    I copied the process.h file.
    Here below is my main.cpp:
    #include <QGuiApplication>
    #include <QQuickView>
    #include <QQmlEngine>
    #include <QtQml>
    #include "process.h"

    int main(int argc, char* argv[])
    {
    QGuiApplication app(argc,argv);
    QQuickView view;
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    // Qt.quit() called in embedded .qml by default only emits
    // quit() signal, so do this (optionally use Qt.exit()).
    QObject::connect(view.engine(), &QQmlEngine::quit,
    qApp, &QGuiApplication::quit);
    qmlRegisterType<Process>("Process",1,0,"Process");

    view.setSource(QUrl("qrc:///main.qml"));
    //view.resize(1000, 480);
    view.show();
    return app.exec();
    

    }

    Here below is my qml file:
    Item {
    id: usbcam2
    signal clicked
    x:10
    y:450
    width : 140
    height: 70

        BorderImage {
            id: buttonImage2
            source: "images/toolbutton.sci"
            width: usbcam2.width
            height: usbcam2.height
        }
        MouseArea {
            id: mouseRegion2
            anchors.fill: buttonImage2
            onClicked: process.start("/bin/cat", [ "/proc/uptime" ]);
        }
        Text {
            id: btnText2
            anchors.fill: buttonImage2
            anchors.margins: 5
            text: "USBcam2"
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
            elide: Text.ElideRight
            color: "white"
            font.bold: true
            style: Text.Raised
            styleColor: "black"
            font.pixelSize: 14
        }
    
    }
    

    }

    When I click the button, the error showed:
    qrc:/main.qml:99: ReferenceError: process is not defined
    what's that meanning?



  • @lxy12345 said in run a sh shell while clicking button in QML with QT5.15.0:

    Here below is my qml file:

    you did not create a Process item

     Process {
            id: process     
        }
    


  • @LeLev I don't really understand this code:
    onReadyRead: text.text = readAll();
    Is onReadyRead a signal? or something like onCliked in button?


  • Qt Champions 2019



  • @jsulm Hi, jsulm. I have tried your post but I misunderstand somewhere maybe.
    I don't understand the singal "onReadyRead", I googled which said that this signal is for TCP/IP??? I am not very sure about that.
    what and where should I modify in your post?


  • Lifetime Qt Champion

    @lxy12345 said in run a sh shell while clicking button in QML with QT5.15.0:

    onReadyRead

    Hi
    Its also for other things.
    " QProcess emits readyRead() when data is available on the current read channel. It also emits readyReadStandardOutput() when new standard output data is available"

    https://doc.qt.io/qt-5/qprocess.html
    in the Communicating via Channels section



  • @jsulm when I tried the code ,
    Text {
    id: text
    }

    Process{
        id: process
         onReadyRead: text.text = readAll();
    }
    Timer {
         interval: 10
         repeat: true
         triggeredOnStart: true
         running: true
         onTriggered: process.start("/bin/cat", [ "/proc/uptime" ]);
     }
    

    it outputs:
    QProcess::start: Process is already running
    QProcess::start: Process is already running
    QProcess::start: Process is already running
    QProcess::start: Process is already running
    QProcess::start: Process is already running
    QProcess::start: Process is already running
    QProcess::start: Process is already running
    QProcess::start: Process is already running

    I think that the shell here is just "cat /proc/uptime", it should output some numbers.
    Maybe I should read and understand Qprocess class first?



  • @lxy12345
    I don't know anything about QML, but

    QProcess::start: Process is already running

    means that you have a QProcess instance where you have set of a start() but it has not yet completed and you have not waited for it, and now you're trying to set off a new start(). You either need to wait for completion of the process instance, or you need to use a new instance of QProcess each time. As I say, i don't know QML how to do that, but that's the cause/reason.


  • Lifetime Qt Champion

    Hi
    Since you set the timer to repeat, it will start a new QProcess each time and
    10 ms might be too short. try raising it so the other Qprocess gets change to finished.

    Timer {
    interval: 10
    repeat: true



  • @mrjj
    Really it ought have a handler for "process finished" signal, to be robust. I don't know how you do that from QML.

    OP might have a go at the naughty:

         onTriggered: process.start("/bin/cat", [ "/proc/uptime" ]); process.waitForFinished();
    

    with all the caveats. I don't know if that would work simpler.


  • Lifetime Qt Champion

    @JonB
    Yes i agree as guessing on execution time is bound to fail in many other cases.



  • @mrjj finally, I found the reason. I should add console.log() to show output . The post is correct, thanks guys



  • @lxy12345 said in run a sh shell while clicking button in QML with QT5.15.0:

    The post is correct

    What post is correct?





  • @lxy12345
    Ah, I see. Yes, that looks reasonable. However, it has no code for waiting for the QProcess::finished signal. I admitted earlier I do not know how QML works with regard to instances. But if it does not create new QProcess instances, and if you re-use the one instance before the cat OS command has completed, you will get the QProcess::start: Process is already running error you showed earlier. Since the /bin/cat /proc/uptime completes "quickly" you may get away with it for a while.

    You may well be using this as just an example to play with QProcess, I don't know. But be aware that if you are really trying to do /bin/cat /proc/uptime , that is a waste of time! All that does is an OS command to read the contents of the file. You would be better doing this by just opening the file /proc/uptime and reading its content from QML, without any QProcess OS command at all.



  • @JonB thanks for replying me. The "cat /proc/uptime" cmd is just an example. I change the cmd in my project, I use this code to open a shell which controls some peripherals.



  • Basically close/kill / terminate the process before running a new process
    here an example I extracted from one of my projects as I keep "bash" process alive till closing the app itself.
    so try to find where you wan to stop the process and close it.

    cmd.h

    #pragma once
    #include <QProcess>
    #include <QVariant>
    
    class Process : public QProcess
    {
        Q_OBJECT
    public:
        explicit Process( QObject* parent = Q_NULLPTR ) : QProcess( parent )
        { }
    
         // If want to use Start with arguments
        Q_INVOKABLE void start( const QString& program, const QVariantList& arguments )
        {
            QStringList args;
    
            // convert QVariantList from QML to QStringList for QProcess
            for ( const auto& temp : arguments ) {
                args << temp.toString();
            }
    
            QProcess::setProcessChannelMode( QProcess::MergedChannels );
            QProcess::start( program, args );
        }
    
        // If wan to start without arguments for Qt > 5.14
        Q_INVOKABLE void start( const QString& program )
        {
    
            QProcess::setProcessChannelMode( QProcess::MergedChannels );
            QProcess::setProgram( program );
            QProcess::start();
            QProcess::open( QProcess::ReadWrite );
        }
    
        Q_INVOKABLE QByteArray readAll() { return QProcess::readAll(); }
        Q_INVOKABLE QByteArray readLine() { return QProcess::readLine(); }
    
         // if you want to write to an active process, not fully tested!
        Q_INVOKABLE qint64 write( const QString& data )
        {
            return QProcess::write( data.toLocal8Bit().data() );
        }
    
    private:
        Q_DISABLE_COPY( Process )
    };
    
    

    In main.h

    qmlRegisterType<Process>( "Process", 1, 0, "Process" );
    

    In main.qml

    import QtQuick 2.15
    import QtQuick.Controls 2.15
    import QtQuick.Layouts 1.15
    import Qt.labs.platform 1.1
    import Process 1.0
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        Component.onCompleted: {
            py.start("bash")
        }
    
        // use py.close() or py.kill(0 or py.terminate() to kill the process
        Component.onDestruction: py.close()
    
        Process {
            id: py
    
            property string output: ""
    
            onStarted: print("Started")
            onFinished: print("Closed")
    
            onErrorOccurred: console.log("Error Ocuured: ", error)
    
            onReadyReadStandardOutput: {
                output = py.readAll()
                term.text += output
            }
        }
    
        Page {
    
            anchors.fill: parent
    
            ScrollView {
                id: view
                anchors.fill: parent
    
                TextArea {
                    id: term
                    textFormat: TextArea.RichText
                }
            }
    
            footer: RowLayout {
                Layout.fillWidth: true
    
                TextField {
                    id: input
                    Layout.fillWidth: true
                }
                Button {
                    text: "Send!"
                    property string command: input.text.trim() + "\n"
                    onClicked: {
                        py.write(command)
                        term.text += ("<br /><b>" + command + " : </b>")
                    }
                }
            }
        }
    }
    
    

    Test Qml Bash App

    I have run cat /proc/uptime as one program command.


Log in to reply