延迟发出信号

3
我希望实现的目标是发出一个信号,但在实际发出之前延迟一段时间。
我的想法是这样的:
emitLater(QObject *obj, QEvent *event, int msDelay)
{
    QTimer...
    connect(timout to obj and event...
    start timer with msDelay...

    //maybe I might need a public timer of some sort but the idea is clear
}

所以这种方法在 QEvent 参数上失败了,因为信号无法转换为 QEvents

我尝试使用几种不同的 std::bind 调用方式,但是我无法让它正常工作:

//the call
std::function<void(void)> signal = std::bind(&CustomPushButton_Header::clicked, &uiWindow->btnHeader_left); //just a random button to connect a valid signal
emitLater(signal, 2000);

//the function
emitLater(std::function<void(void)>, int msDelay)
{
    qDebug() << "hi";
}

我希望有一个适用于所有对象和信号的解决方案...也许只需要调用函数的部分。我认为延迟会很容易解决。

或者您有不同甚至更简单的方法。那将是很好的。

我查看了一些来源:


你能否编辑你的问题,提供一个更完整的用例,展示你想要实现什么。特别是,所展示的代码似乎重复使用了一个 QEvent,这是行不通的,因为当控制权返回事件循环时,QEvent 将被删除。一般来说,如果你有一个函数对象 f,想要在 ms 毫秒后调用它,你可以使用 QTimer::singleShot(ms, f) - G.M.
哦,是的,这就是我正在寻找的 :) 谢谢,伙计,现在我很开心。你也可以发布解决方案 :) - Michael1248
3个回答

2

有一个函数QTimer::singleShot(),可以在一定时间后触发信号/槽。我是这样实现的:

void MyWindow::emitLater(const QObject *obj, const char *signalOrSlot, int msDelay)
{
    //use QTimer::singleShot() to trigger the signal/slot after a certain amount of time
    QTimer::singleShot(msDelay, obj, signalOrSlot);
}

并像这样使用它:

void MyWindow::on_pushButton_5_clicked()
{
    //trigger the "doing fancy stuff" function after 5 seconds (= 5000 ms)
    emitLater(this, SLOT(setText()), 5000);
}

void MyWindow::setText()
{
    //do some fancy stuff
    ui->label_3->setText("Hello World!");
}

另一种方法可以在ΦXocę 웃 Пepeúpa ツ的答案中找到,它使用一个lambda表达式作为QTimer::timout()信号的调用函数:

void MyWindow::on_pushButton_5_clicked()
{
    //initialize timer
    QTimer* t= new QTimer(this);
    t->setSingleShot(true);

    //do some fancy stuff after timeout
    connect(t, &QTimer::timeout, [this]()
    {
        ui->label_3->setText("Hello World!");
    });

    //start the timer with delay of 5 seconds (= 5000 ms)
    t->start(5000);
}

1

您可以使用计时器来实现此功能,在上面的示例中,您可以通过响应按钮中的单击槽来定义一个QTimer,并连接到定时器超时时调用的内容(在本例中是lambda函数),然后启动计时器:)

void MyWindow::on_pushButton_5_clicked()
{
    QTimer* t= new QTimer(this);
    connect(t, &QTimer::timeout, [this](){ui->label_3->setText("done!");});
    t->start(5000); //after 5 seconds
}

1
是的,那是一个解决方案。现在我使用 QTimer::singleShot(1000, myClass, SLOT(mySlot())),它非常好用并且容易使用。 - Michael1248
1
你能将这个答案标记为答案吗?如果你认为你的解决方案更好,你也可以回答自己的问题 :) - user12927872
在这里使用singleShot()更好,因为在解决方案中,计时器实例会累积直到MyWindow父对象被销毁。 - Frank Osterfeld

0

这是我的使用PythonPySide2的实现。

首先,我创建了一个类:

class DelayedSignal:
    def __init__(self, timeout, func):
        self.timeout = timeout
        self.func = func
        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.setInterval(1000 * timeout)
        self.timer.timeout.connect(func)

    @Slot()
    def __call__(self):
        self.timer.stop()
        self.timer.start()

这是代码的其余部分,因此所有内容在一起就是一个最小工作示例:
from PySide2.QtCore import QTimer, Slot
from PySide2.QtWidgets import QPushButton, QWidget, QHBoxLayout


class AppWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.btn = QPushButton("PRESS", self)
        self.setLayout(QHBoxLayout())
        self.layout().addWidget(self.btn)
        self.delayed_signal = DelayedSignal(timeout=2, func=self.target)
        self.btn.clicked.connect(self.delayed_signal)

    @Slot()
    def target(self):
        print("END")


if __name__ == '__main__':
    import sys

    from PySide2.QtWidgets import QApplication

    app = QApplication(sys.argv)
    window = AppWindow()
    window.show()
    sys.exit(app.exec_())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接