【问题标题】:Wait for buttonPressed() slot to finish before executing buttonReleased()在执行 buttonReleased() 之前等待 buttonPressed() 插槽完成
【发布时间】:2016-05-23 05:43:42
【问题描述】:

我有一个 QPushButton,它对 pressed()released() 信号执行冗长的操作。在执行buttonReleased() 槽的操作之前,如何确保我完成了buttonPressed() 槽的所有操作?

我尝试过使用 QMutex,但是当尝试在按钮释放时锁定时,程序似乎陷入了无限循环,此时互斥锁仍被 buttonPressed() 函数锁定:

我的主窗口.h:

#include <QMutex>

// ...

QMutex mutex;

我的主窗口.cpp:

#include <QEventLoop>
#include <QTimer>

// ...

// In the main window constructor:
connect(myButton, SIGNAL(pressed()), this, SLOT(buttonPressed()));
connect(myButton, SIGNAL(released()), this, SLOT(buttonReleased()));

// ...

void MyMainWindow::buttonPressed()
{
    mutex.lock();

    // Here, I do the lengthy stuff, which is simulated by a loop
    // that waits some time.
    QEventLoop loop;
    QTimer::singleShot(1000, &loop, SLOT(quit()));
    loop.exec();

    mutex.unlock();
}

void MyMainWindow::buttonReleased()
{
    mutex.lock();

    // ... (some stuff)

    mutex.unlock();
}

【问题讨论】:

  • 您能否更详细地解释一下,您要达到什么目的?我觉得这个逻辑很奇怪。为什么在按下按钮和松开按钮时都会进行冗长的操作?
  • @thuga 该按钮控制步进电机:按下按钮时打开,释放点击时关闭。当我按下按钮时,我会向控制电机的电子设备发送几个命令。这需要时间,因为我必须等待电子设备的响应。此外,电机需要缓慢加速以避免机械损坏,这涉及到启动它的命令序列中的实际 QTimer。当我释放点击时,我想发送其他命令来停止电机,但这应该在buttonPressed() 结束后完成。
  • 你所有的槽都在同一个线程中执行,所以使用互斥锁是没用的。
  • 添加某种命令队列。当您按下按钮时,将您使用的所有命令添加到队列中。释放按钮时,将停止命令添加到队列中。不要等待任何事情,只需回复您从电子设备收到的消息。每次命令完成时,您都会从队列中获取下一个命令并将其发送到您的电机。

标签: c++ qt mutex signals-slots qmutex


【解决方案1】:

一般使用互斥锁是一种线程同步机制,这里不需要线程同步,因为你在同一个线程中。否则,我建议使用 QWaitCondition 来等待标志/互斥体发生变化(即表明您的条件现在可以继续了)。

在您的情况下,您可以在“buttonPressed”操作完成后发出一个信号(即当您的计时器结束时?)。如果 buttonPressed() 函数的结束是您想要执行 buttonRelease() 函数的时间,那么您可以简单地使用 Qt::QueuedConnection 来确保事件的正确顺序(我通常不喜欢直接连接,因为它们的行为就像函数调用(甚至中断——就像我认为发生在你身上的那样)。所以以下更改可能会以一种简单的方式为你解决这个问题:

// In the main window constructor:
connect(myButton, SIGNAL(pressed()), this, SLOT(buttonPressed()), Qt::QueuedConnection);
connect(myButton, SIGNAL(released()), this, SLOT(buttonReleased()), Qt::QueuedConnection);

我不确定执行你的事件循环来“模拟”你的“长时间”是否会起作用....但是如果你做一些更像下面的事情来模拟你的长时间执行:

QElapsedTimer elapsedTime;
elapsedTime.start();
while (elapsedTime.elapsed() < 1000) // millisecs
{
    // wait....
}

如果这不起作用,那么只需在 buttonPressed() 结束时发出一个信号并在 buttonReleased() 中设置一个标志,这样:

void MyMainWindow::buttonPressed()
{
    // actions here
    emit buttonPressedDone();
}

void MyMainWindow::buttonReleased()
{
    btnReleased = true;
}

void MyMainWindow::buttonPressedCompleted()
{
    if (btnReleased )
    {
        // Do button released actions here
        btnReleased  = false;
    }
    // I am assuming that if the flag is not set then you don't want to do anything... but up to you...
}

并连接 buttonPressedDone --> buttonPressedCompleted

还有更多选项...这些只是为您提供的几个选项...

【讨论】:

    【解决方案2】:

    是否需要连接才能释放按钮的 SLOT?您只需连接到您的QEventLoopdestroyed() SIGNAL 并致电buttonReleased() SLOT。

    QEventLoop loop;
    QTimer::singleShot(1000, &loop, SLOT(quit()));
    connect(&loop, &QEventLoop::destroyed, this, &MyMainWindow::buttonReleased);
    loop.exec();
    

    由评论编辑:

    QEventLoop loop;
    QTimer::singleShot(1000, &loop, SLOT(quit()));
    connect(&loop, &QEventLoop::destroyed, [=] {
        connect(myButton, &QPushButton::released, this, &MyMainWindow::buttonReleased); 
    });
    loop.exec();
    

    【讨论】:

    • 如果我长按按钮释放之前不会执行buttonReleased()吗?
    • 谢谢。我在connect 行收到“错误:C3260:',':在 lambda 正文之前跳过意外令牌”。另外,我不明白那行的语法。
    • 对不起,我写错了代码。已经编辑好了。关于语法你可以阅读here
    猜你喜欢
    • 2013-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 1970-01-01
    • 2017-05-31
    • 2017-02-02
    • 1970-01-01
    相关资源
    最近更新 更多