从QML应用程序运行系统命令

我想在我的应用程序中运行一个系统命令。它应该使用SSH在远程服务器上运行一个命令。但这并不是重点。重点是我不知道如何从应用程序中运行任何类型的命令。我在邮件列表中询问过,他们建议我使用C++构建一个QML扩展。但我不懂C++,而且似乎我必须学习很多东西才能运行一个简单的命令。
在Python(以及PHP)中,运行系统命令很容易。在我的Touch应用程序中是否有其他方法来实现,或者是否有人可以提供更多帮助?或者也许对我的问题有更好的解决方案?

1你能删除与你的问题核心无关的所有内容吗?比如"Minecraft服务器"、你是如何学习这个的"只是为了好玩"等等?我之所以问这个是因为这些细节离题并且相当分散注意力,恕我直言。 - Anon
5个回答

这不是QML支持的东西,典型的解决方案是编写一个C++插件来处理这种情况。
然而,SDK团队正在计划为QML应用开发者提供各种扩展,这可能是他们在通用插件中实现的一项功能,您可以使用。

2非常感谢!我已经开始寻找一种调用Python脚本的方法,但是我只能找到一个运行QML的Python脚本,而不是相反的情况。 - Daniel Holm
我最后做的是对我的WebUI进行了一些更改,实现了与新应用程序相同的功能,并使用XML从中提取所需的信息。非常不错。 - Daniel Holm
2我在14.04版本中尝试了QProcess启动器的概念,效果很好:http://askubuntu.com/a/446736/20275 - int_ua
@mhall119 如果我错了,请纠正我,但是由于AppArmor的限制,你实际上无法在手机上使用QML来做这个。它会阻止你这样做。 - Anon

更新:对于14.04,请参考int_ua提供的简化答案。

原始文本:

http://talk.maemo.org/showthread.php?t=87580上有一个关于如何将扩展添加到QML的基本概述。我决定使用ubuntu-sdk来尝试一下,这与之略有不同。我将在下面进行记录。

对于这个项目,我在QtCreator中选择了Ubuntu Touch/Simple UI with C++ Backend。这将创建一个包含两个独立部分的项目,即后端和用QML编写的触摸界面前端。我们将为后端添加两个文件,用于Launcher类。

launcher.h:

#ifndef LAUNCHER_H
#define LAUNCHER_H

#include <QObject>
#include <QProcess>

class Launcher : public QObject
{
    Q_OBJECT
public:
    explicit Launcher(QObject *parent = 0);
    Q_INVOKABLE QString launch(const QString &program);

private:
    QProcess *m_process;
};

#endif // LAUNCHER_H

launcher.cpp:

#include "launcher.h"

Launcher::Launcher(QObject *parent) :
    QObject(parent),
    m_process(new QProcess(this))
{
}

QString Launcher::launch(const QString &program)
{
    m_process->start(program);
    m_process->waitForFinished(-1);
    QByteArray bytes = m_process->readAllStandardOutput();
    QString output = QString::fromLocal8Bit(bytes);
    return output;
}

这个类简单地使用QProcess来执行一个程序,等待它完成,读取它的stdout,并将其作为字符串返回。
接下来,我们需要修改backend/backend.cpp以包含这个类。这需要两行代码。追加一个include:
#include "launcher.h"

BackendPlugin::registerTypes中添加一行代码。
qmlRegisterType<Launcher>(uri, 1, 0, "Launcher");

应该已经有一个关于MyType的行,这是包含的示例。之后我们应该能够构建后端。唯一剩下的就是在main.qml文件中使用它了。为此,我添加了一行:

Launcher { id: myLauncher }

并且在按钮的onClick处理程序中设置:
myType.helloWorld = myLauncher.launch("date");

此时,唯一剩下的就是启动它并进行测试。在这里,我遇到了一个问题,因为QtCreator似乎默认情况下没有正确设置所有内容。作为解决办法,在终端中导航到您的QtCreator项目目录,并执行以下操作:
mkdir -p Ubuntu/Example

然后将libUbuntuExample.so文件从ProjectBuildDir/backend复制到Ubuntu/Example,将qmldir文件从ProjectName/backend/qmldir复制过来。然后你就可以运行了:
qmlscene -I . ProjectName/touchui/main.qml

我相信可能有一种简单的方法来搭建这一切,使得构建/运行变得顺利。

现在只适用于14.04版本:http://askubuntu.com/a/446736/20275 - int_ua

Ubuntu 14.04

QProcess启动器类型的概念在Trusty中与ubuntu-sdk-team PPA中正常工作。只需要创建一个QML扩展库 + 带有选项卡的用户界面项目(项目名称暂时不要使用连字符),替换内容:

mytype.h

#ifndef LAUNCHER_H
#define LAUNCHER_H

#include <QObject>
#include <QProcess>

class Launcher : public QObject
{
    Q_OBJECT

public:
    explicit Launcher(QObject *parent = 0);
    ~Launcher();
    Q_INVOKABLE QString launch(const QString &program);

protected:
    QProcess *m_process;
};

#endif // LAUNCHER_H

mytype.cpp

#include "mytype.h"

Launcher::Launcher(QObject *parent) :
    QObject(parent),
    m_process(new QProcess(this))
{

}

QString Launcher::launch(const QString &program)
{
    m_process->start(program);
    m_process->waitForFinished(-1);
    QByteArray bytes = m_process->readAllStandardOutput();
    QString output = QString::fromLocal8Bit(bytes);
    return output;
}

Launcher::~Launcher() {

}

backend.cpp 中的 qmlRegisterType 更改为
qmlRegisterType<Launcher>(uri, 1, 0, "Launcher");

接下来,只需从QML文件中清除所有的MyType残留,并添加。
        Rectangle {

          Launcher {
             id: qprocess
          }

          Text {
            anchors.centerIn: parent
            text: qprocess.launch("which bash")
          }
        }

无论你喜欢在哪里
import projectname 1.0

在开始。

可选

我也使用这个包装器:

function exec(command) {
    return qprocess.launch("sh -c \"" + command + " < /dev/null \"")
}

如果您需要root访问权限,请添加pkexec

1我只是想确认一下,这个解决方案对我来说非常有效。无论您输入什么命令,它的输出都会显示在矩形框中。 - Anon

你真的不需要对C++有很多了解才能使用终端命令。只需将以下内容放入以.cpp结尾的任何文件中,例如runPython.cpp。
#include <stdlib.h>

int main ()
{
    system("cd /home/user/path/to/script");
    system("python3 myScript.py");
    return 0;
}

现在你需要弄清楚的就是如何在QML中运行C++代码,但我相信这方面的文档已经很完善了。
请注意,您可以按照相同的语法system("linux command");添加任何Linux命令。
希望对你有所帮助!

你应该使用PlasmaCore.DataSource和引擎 "executable",就像这个example所示。

请注意,调用是异步的。因此,当命令完成时会发送一个信号,代码的另一部分会接收信号并继续执行下一步想要执行的操作。