2

当我使用 emit 更新 GUI 上的状态时,我的应用程序冻结了。

我想知道原因或如何避免这种冻结。感谢您的评论。

我的测试环境

演示应用程序如下所示。

#!/usr/bin/env ruby
# encoding: UTF-8
#

require 'Qt'

class App < Qt::MainWindow
    signals 'test()'
    slots   'on_test()'

    def initialize
        super

        @label = Qt::Label.new
        self.centralWidget = @label

        self.show

        connect self, SIGNAL('test()'), SLOT('on_test()')
        start_count
    end

    def start_count
        Thread.new do
            loop {
                emit test()
            }
        end
    end

    def on_test()
        @label.text = @label.text.to_i + 1
    end
end

app = Qt::Application.new(ARGV)
App.new
app.exec

@hyde 谢谢你的回答。

qtbindings 的解决方案 2 似乎没有帮助。

connect self, SIGNAL('test()'), SLOT('on_test()')
=> 
connect self, SIGNAL('test()'), SLOT('on_test()'), Qt::BlockingQueuedConnection

解决方案1经过测试,应用程序运行流畅。

解决方案1的代码:

#!/usr/bin/env ruby
# encoding: UTF-8
#

require 'Qt'

class App < Qt::MainWindow
    slots   'on_test()'

    def initialize
        super

        @label = Qt::Label.new
        self.centralWidget = @label

        self.show

        @c = Qt::AtomicInt.new

        start_count
        start_timer
    end

    def start_count
        Thread.new do
            loop {
                @c.fetchAndAddRelaxed(1)
            }
        end
    end

    def start_timer
        t = Qt::Timer.new(self)
        t.start(16)
        connect t, SIGNAL('timeout()'), SLOT('on_test()')
    end

    def on_test()
        @label.text = @c.fetchAndAddRelaxed(0) + 1
    end
end

app = Qt::Application.new(ARGV)
App.new
app.exec
4

2 回答 2

2

推测原因:Qt 主线程事件循环永远不会处理应用程序事件,因为它太忙于传递排队的信号。信号是排队的,因为它们在线程之间,所以槽在正确的线程中被调用,独立于发射线程(在这种情况下它们必须排队,因为槽操作 GUI 对象,这仅允许从主线程)


有几种方法可以解决这个问题。我真的不了解 Ruby 或它的 Qt 绑定,所以这里只是一个粗略的概述:

解决方案1:让线程循环尽可能快地运行:

  1. 创建一个原子变量。要么使用一些 Ruby 类型,要么QAtomicInt(我假设使用 Ruby Qt 绑定是可能的)。

  2. 在您的线程循环中,只需增加原子变量,而不是发出信号。

  3. 添加一个QTimer用于@label.text以所需的时间间隔更新。如果用户应该能够读取该数字,我建议间隔 500 毫秒。对于 ~60 fps 的更新率,最小合理间隔大约是 16 毫秒。

  4. 将计时器超时连接到on_test并获取原子整数的值以更新文本。

这样,数值的更新与显示无关。

解决方案 2:使线程阻塞,直到发出的信号实际被传递:

使用连接类型BlockingQueuedConnection(注意:不要与单线程一起使用)。将该连接类型添加到信号连接语句(但是您使用 Ruby Qt 执行此操作)。然后发出线程将阻塞,直到实际调用目标槽,因此信号只会以它们可以处理的速率发出,不会更快。

于 2014-07-16T12:21:01.310 回答
0

您的应用程序冻结导致您在无限循环中发出信号,但不是发出的原因

于 2014-07-15T19:05:22.363 回答