PDA

View Full Version : Need to fetch data from a Bash Script and display in a QT app.



Aveek
22nd June 2018, 16:03
Hi,

I am very new to QT application programming. I need an urgent help.

The main idea / intenTion is to convert my bash script to some application with UI. My bash script currently looks like below:

echo "Listing the Devices connected over USB"
system_profiler SPUSBDataType | egrep 'SomeName [A-Za-z0-9]+:'
read device_name

device_serial_number=$((system_profiler SPUSBDataType) | (sed -n "/$device_name/,/Serial/p") | (grep "Serial Number:") | (awk -F ": " '{print $2}'))

echo "The Device Serial Number is: "
echo $device_serial_number



exit
fi

The script basically gives the list of USB connected devices and displays the same in the terminal. So now I need to convert the same into a UI based application which will run exactly the same command and display the list in the QT Application UI.

Need suggestions on how to implement the same.


Regards,
Aveek

d_stranz
22nd June 2018, 17:12
Use QProcess to execute your bash script. Before you start the execution of the script, use QProcess::setReadChannel() to connect stdout to the process read channel. Connect a slot to the QProcess::readyReadStandardOutput() signal. When your bash script is done executing, QProcess will emit that signal, and you can then read the contents of stdout that have been captured using QProcess::readAllStandardOutput().

Aveek
23rd June 2018, 07:47
Hi,

I am tried to do something like this:

void MainWindow:: on_pushButton_clicked()
{
QProcess proc;
proc.start("/bin/bash", QStringList() << "-c" << cmd);
proc.execute("system_profiler SPUSBDataType | egrep 'Lex [A-Za-z0-9]+:'");

proc.waitForFinished();

QByteArray output = proc.readAll();
proc.close();

QTextEdit *txt = new QTextEdit();

txt->setText(output);
txt->show();
}



This is giving me the data. Do you see any problem in doing it this way?

Secondly how can I execute commands like this:

system_profiler SPUSBDataType | sed -n '/Lex/,/Serial/p' | grep "Serial Number:" | awk -F ": " '{print $2}'

Do I need to change / to // and what should I do with '{print $2}'


Regards,
Aveek

d_stranz
23rd June 2018, 16:09
proc.execute("system_profiler SPUSBDataType | egrep 'Lex [A-Za-z0-9]+:'");

QProcess::execute() is a static member function of the QProcess class. If you call it the way you do, it completely ignores the "proc" instance you have created on the stack, so there is no way that proc.readAll() will retrieve the result of the execute() method. You are basically creating two completely independent processes (using start() and execute()) that have no relationship to each other.

I think you need to create a command string from the commands you want bash to execute and add it to the QStringList you pass as the arguments to start(). You do not need to convert / to // (you are confusing that with "\"), but you -do- need to escape any special characters in the string, such as the embedded quotes in the grep command.

Aveek
26th June 2018, 17:16
Hi,

Thanks for all your inputs. It was really helpful. I have changed my code like this. Please let me know if it is fine.

void MainWindow::function()
{
proc = new QProcess(this);

QString cmd = "system_profiler SPUSBDataType | egrep 'Name [A-Za-z0-9]+:'";
connect(proc, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput()));

proc->start("/bin/bash", QStringList() << "-c" << cmd);
proc->waitForFinished();
}

void MainWindow::readOutput()
{
qDebug()<<"Read";
QByteArray byteArray = proc->readAllStandardOutput();
QStringList strLines = QString(byteArray).split("\n");

foreach (QString line, strLines){

ui->listWidget->addItem(line);
}

}


Secondly now if I need to execute one more command should I use the same proc instance or create a new one?

Can the following be done?

void MainWindow::function()
{
proc = new QProcess(this);

QString cmd1 = "some command";
connect(proc, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput1()));

proc->start("/bin/bash", QStringList() << "-c" << cmd1);
proc->waitForFinished();


QString cmd2 = "some other command";
connect(proc, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput2()));

proc->start("/bin/bash", QStringList() << "-c" << cmd2);
proc->waitForFinished();

}

d_stranz
26th June 2018, 21:51
I think that will work. However, you need to call disconnect() to remove the slot for the first readyRead signal, otherwise both slots will be called for the second process. In addition, if you are going to re-use the same QProcess instance, do not create it in function(), create it in the MainWindow constructor. Your code now creates a new QProcess every time function() is called, and ends up with a memory leak because you overwrite the "proc" variable that holds the pointer to QProcess.

Alternatively, simply create the QProcess as a temporary variable on the stack in function(). It will automatically be deleted when the method exits. If you need the pointer to QProcess in the readyRead slot, use QObject::sender() to retrieve the pointer, and qobject_cast<> to convert it to a QProcess pointer.



void MainWindow::function()
{
QProcess proc;

connect( &proc, &QProcess::readyReadStandardOutput, this, &MainWindow::someSlot1 );
proc.start( whatever, whatever );
proc.waitForFinished();
disconnect( &proc, &QProcess::readyReadStandardOutput, this, &MainWindow::someSlot1 );

connect( &proc, &QProcess::readyReadStandardOutput, this, &MainWindow::someSlot2 );
proc.start( whatever, whatever );
proc.waitForFinished();
disconnect( &proc, &QProcess::readyReadStandardOutput, this, &MainWindow::someSlot2 );

// ...
}

void MainWindow::someSlot1()
{
QProcess * pProc = qobject_cast< QProcess * >( sender() );
if ( pProc != nullptr )
{
// ...
}
}

Aveek
27th June 2018, 13:12
Thanks.....for all the inputs.


Reagrds,
Aveek