首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >当连接到其点击()信号的插槽正在运行时,如何使按钮响应?

当连接到其点击()信号的插槽正在运行时,如何使按钮响应?
EN

Stack Overflow用户
提问于 2016-08-31 14:18:45
回答 2查看 289关注 0票数 0

我有一个按钮,它的一个插槽连接到它点击的()信号。

插槽是一项耗时的任务,耗时数秒。

当尝试在插槽结束前重新点击按钮时,我做不到。

当运行连接到其点击()信号的插槽时,如何使按钮响应?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-08-31 16:05:03

在GUI中运行的任何东西都不能阻塞,也不需要花费很多时间来完成。不要在GUI线程中做长时间运行的事情。这会解决你的问题。

现在你可能会问:那么,我该如何做长期的事情呢?您可以在其他线程中执行这些操作,但这并不意味着必须看到QThread的实例。

你必须隔离数据和对它的操作。假设我们有以下课程:

代码语言:javascript
运行
复制
class MyData {
public:
    MyData() {}
    /// Loads and pre-processes the data
    bool load(const QString & file) { QThread::sleep(5); return true; }
    /// Transforms the data
    void transform() { QThread::sleep(2); }
};

using MyDataPtr = std::shared_ptr<MyData>;
Q_DECLARE_METATYPE(MyDataPtr)

int main(...) { qRegisterMetaType<MyDataPtr>(); ... }

为了使其更加通用,我们假设MyData复制和移动成本很高。

如果操作属于“生成新数据”类型,例如读取文件、处理数据和构建数据结构,则可以在工作线程中实例化数据并将其传递给GUI线程:

代码语言:javascript
运行
复制
class GuiClass : public QWidget {
    Q_OBJECT
    MyDataPtr data;
    Q_SIGNAL void hasData(const MyDataPtr &);
public:
    GuiClass() {
        connect(this, &GuiClass::hasData,
                this, [this](const MyDataPtr & newData){ data = newData; });
    }
    Q_SLOT void makeData(const QString & file) {
        QtConcurrent::run([=]{
            auto data = std::make_shared<MyData>();
            if (data->load(file))
                emit hasData(data);
        });
    }

如果您需要转换已经到位的数据,一些常见的方法是:

  1. 复制数据,在工作线程中转换数据,然后在转换后将其交换出去。 Q_SLOT void transformData1() { MyDataPtr copy = std::make_shared(*data);QtConcurrent::run(={ MyDataPtr->transform();发出hasData(拷贝);});}
  2. 暂时删除数据,将其转换,然后将其放回。UI必须处理丢失的数据,例如,当data为null时显示一个“繁忙”指示符。 Q_SLOT void transformData2() { MyDataPtr data = this->data;this->data.reset();QtConcurrent::run(={ data->MyDataPtr();MyDataPtr hasData(data);});}

如果MyData很容易移动,并且默认值的含义为“空”/“空”,则可以按值保留它:

代码语言:javascript
运行
复制
class GuiClassVal : public QWidget {
    Q_OBJECT
    MyData data;
    struct DataEvent : public QEvent {
        static const QEvent::Type type;
        MyData data;
        DataEvent(const MyData & data) : QEvent{type}, data{data} {}
        DataEvent(MyData && data) : QEvent{type}, data{std::move(data)} {}
    };
    /// This method is thread-safe.
    void setData(MyData && data) {
        QCoreApplication::postEvent(this, new DataEvent{std::move(data)});
    }
    /// This method is thread-safe.
    void setData(const MyData & data) {
        QCoreApplication::postEvent(this, new DataEvent{data});
    }
    bool event(QEvent *event) override {
        if (event->type() == DataEvent::type) {
            data.~MyData();
            new (&data) MyData{std::move(static_cast<DataEvent*>(event)->data)};
            return true;
        }
        return QWidget::event(event);
    }
    void transformInEvent(DataEvent * ev){
        ev->data.transform();
        QCoreApplication::postEvent(this, ev);
    };
public:
    Q_SLOT void makeData(const QString & file) {
        QtConcurrent::run([=]{
            MyData data;
            if (data.load(file)) setData(std::move(data));
        });
    }
    Q_SLOT void transformData1() {
        QtConcurrent::run(this, &GuiClassVal::transformInEvent, new DataEvent{data});
    }
    Q_SLOT void transformData2() {
        QtConcurrent::run(this, &GuiClassVal::transformInEvent, new DataEvent{std::move(data)});
    }
};
const QEvent::Type GuiClassVal::DataEvent::type = (QEvent::Type)QEvent::registerEventType();

当然,以事件为基础的魔法酱是可以被考虑出来的。

票数 4
EN

Stack Overflow用户

发布于 2016-08-31 14:31:51

你有两个高级别的选择:

  • 将耗时的选择转化为线程:最简单的方法是使用QtConcurrent::run(),在GUI方面,您需要在执行操作时适当地处理所有的GUI事件。
  • 在耗时的代码中,经常调用QApplication::processEvents(),使GUI保持响应性(例如,在循环内部):如果耗时的操作是第三方代码,则这将无法工作,而且在执行长期运行的任务时,仍然需要解决如何处理GUI操作。

你会发现,到目前为止,第一种选择比第二种更好,FWIW。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39252150

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档