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
-
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?
-
@lxy12345
Hi
did you try
http://71.254.128.33/qml/process.html
-
@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: 70BorderImage { 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?
-
@lxy12345 said in run a sh shell while clicking button in QML with QT5.15.0:
Is onReadyRead a signal?
yes
-
@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?
-
@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 runningI 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, butQProcess::start: Process is already running
means that you have a
QProcess
instance where you have set of astart()
but it has not yet completed and you have not waited for it, and now you're trying to set off a newstart()
. You either need to wait for completion of the process instance, or you need to use a new instance ofQProcess
each time. As I say, i don't know QML how to do that, but that's the cause/reason.
-
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.
-
@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?
-
@JonB http://71.254.128.33/qml/process.html This post.
-
@lxy12345
Ah, I see. Yes, that looks reasonable. However, it has no code for waiting for theQProcess::finished
signal. I admitted earlier I do not know how QML works with regard to instances. But if it does not create newQProcess
instances, and if you re-use the one instance before thecat
OS command has completed, you will get theQProcess::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 anyQProcess
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( qPrintable( 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>") } } } } }
I have run
cat /proc/uptime
as one program command.
-
Here is an update to run multiple process as a replay to reddit QA
import QtQuick 2.15 import QtQuick.Controls 2.15 import Process 1.0 ApplicationWindow { visible: true width: 640 height: 480 title: "Run a sh file in CMD!" // prcoess to run and read .sh file Process { id: cmd property string output: "" onStarted: print("Started") // Once reading the .sh finish we will run another process to run the notify-send! onFinished: notify.start("notify-send", ["-t", "4000", "Reading .sh file Completed!"]) onErrorOccurred: console.log("Error Ocuured: ", error) onReadyReadStandardOutput: { output = cmd.readAll() txt.text += output } } // process to run firefox Process { id: fireFox property string output: "" // start notify process once we run firefox! onStarted: notify.start("notify-send", ["-t", "4000", "Starting FireFox!"]) onFinished: print("Closed") onErrorOccurred: console.log("Error Ocuured: ", error) onReadyReadStandardOutput: { output = cmd.readAll() console.log("READING CONSOLE: ", output) } } // process to run notify-osd !! Process { id: notify property string output: "" onStarted: print("Started") onFinished: print("Closed") onErrorOccurred: console.log("Error Ocuured: ", error) onReadyReadStandardOutput: { output = cmd.readAll() console.log("READING CONSOLE: ", output) } } Column { anchors.centerIn: parent TextArea { id: txt text: "" } Row { Button { text: "Run SH!" onClicked: cmd.start("/home/foxoman/file.sh") } Button { text: "Run FireFox!" onClicked: fireFox.start("firefox") } Button { text: "Run Notify" onClicked: notify.start( "notify-send", ["-t", "4000", "OSD Notify From Command Line!"]) } } } }