2

エミットを使用して 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 に答える