如何在QT应用程序中的嵌入式Python中休眠

1 投票
1 回答
684 浏览
提问于 2025-04-16 18:29

我正在把Python嵌入到一个QT图形界面应用程序中。我在我的UI文件里给一个按钮设置了信号,当这个按钮被点击时,我就会运行一个脚本。

这种方法在使用这个链接里的方法时是有效的。

我还在嵌入的模块中添加了一些函数,具体可以参考那个页面的5.4节。我想在Python脚本中添加一些延迟,但我不想用sleep,因为这样会让整个应用程序都停下来。我想应该可以用QTimer来实现,让Python脚本在一段时间后继续运行,但我不太明白该怎么做。

我觉得我离解决方案很近了,所以如果可以的话,我不想再添加线程或者其他框架,比如PythonQT或Boost。

这里是相关的代码片段:

    static PyObject* openDoor(PyObject *self, PyObject *args)
    {
        int value1 = 0;
        if (!PyArg_ParseTuple(args, "l", &value1))
            return Py_BuildValue("i", -1);

    opendoor(value1)
    return PyLong_FromLong(value1);
}

static PyObject* mysleep(PyObject *self, PyObject *args)
{
    int value1 = 0;
    if (!PyArg_ParseTuple(args, "l", &value1))
        return Py_BuildValue("i", -1);
// this does not work !!!
//  QTimer slideShowtimer = new QTimer(this);
//  connect(slideShowtimer, SIGNAL(timeout()), this, SLOT(slideShowHelper()));
//  slideShowtimer->start(5000);


    return PyLong_FromLong(value1);
}


static PyMethodDef EmbMethods[] = {
        {"openDoor", openDoor, METH_VARARGS,  "."},
        {"closeDoor", closeDoor, METH_VARARGS,  "."},
        {"sleep", mysleep, METH_VARARGS,  "Sleep."},
    {NULL, NULL, 0, NULL}
};

static PyModuleDef EmbModule = {
    PyModuleDef_HEAD_INIT, "obu", NULL, -1, EmbMethods,
    NULL, NULL, NULL, NULL
};

static PyObject*
PyInit_emb(void)
{
    return PyModule_Create(&EmbModule);
}

// taken from python docs
void MainWindow::on_ScriptButton_clicked()
{
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pValue;
    int i;

    PyImport_AppendInittab("emb", &PyInit_emb);
    Py_Initialize();

    pName = PyUnicode_FromString("multiply");
    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) {
        pFunc = PyObject_GetAttrString(pModule, "run");

        if (pFunc && PyCallable_Check(pFunc)) {
            pArgs = PyTuple_New(1);
            for (i = 0; i < 1; ++i) {
                pValue = PyLong_FromVoidPtr(this);
                if (!pValue) {
                    Py_DECREF(pArgs);
                    Py_DECREF(pModule);
                    fprintf(stderr, "Cannot convert argument\n");
                }
                PyTuple_SetItem(pArgs, i, pValue);
            }
            pValue = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pValue != NULL) {
                printf("Result of call: %ld\n", PyLong_AsLong(pValue));
                Py_DECREF(pValue);
            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                fprintf(stderr,"Call failed\n");
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \n");
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
        ;
    }
    else {
        PyErr_Print();
        fprintf(stderr, "002 Failed to load \n");
    }
    Py_Finalize();
}

1 个回答

0

这个回答是一般性的,可能不适用于QT框架。(我自己不使用QT)

之所以说sleep函数不起作用,是因为它在C++中的表现大致如下:

static PyObject* mysleep(PyObject *self, PyObject *args)
{
    int secs = 0;
    if (!PyArg_ParseTuple(args, "l", &secs))
        return Py_BuildValue("i", -1);
   long start = gettimestamp(); // This function should return a unix timestamp
   long now = start;
   while (now < start + secs) {
     now = gettimestamp(); }
   return PyLong_FromLong(now-start); }

注意,函数返回的最好是程序实际睡眠的时间,而不是你输入的值。因为你可能需要在代码中知道这个时间段。

这个函数会被Python调用。

但是在图形用户界面(GUI)环境中,你还需要不断检查和处理可能发生的事件,而这个函数并没有做到这一点。

所以你需要一个新的函数,应该像这样:

static PyObject* mysleep(PyObject *self, PyObject *args)
{
    int secs = 0;
    if (!PyArg_ParseTuple(args, "l", &secs))
        return Py_BuildValue("i", -1);
   long start = gettimestamp(); // This function should return a unix timestamp
   long now = start;
   while (now < start + secs) {
     handleEvents(); // This function makes the framework check, and run events in they have occurred it will be framework spefic
     now = gettimestamp(); }
   return PyLong_FromLong(now-start); }

你应该查看QT的文档,看看是否有办法用QT实现handleEvents()函数,这个解决方案应该能解决你的问题。

注意:这会让计算机至少等待n秒,如果正在处理某个事件,必须在循环再次检查时间之前完成这个事件。

另外,handleEvents()应该只允许运行一个事件,接下来才是事件调用列表中的下一个事件,否则它不会返回,直到所有事件都被处理完。

撰写回答