ryanmelt / qtbindings

An easy to install gem version of the Ruby bindings to Qt
http://github.com/ryanmelt/qtbindings
Other
340 stars 63 forks source link

Ruby threads don't run correctly #126

Closed wolfmanjm closed 9 years ago

wolfmanjm commented 9 years ago

I seem to have run into an issue with the current version of the gem on linux x32 (Ubuntu 14.04). (Running ruby 2.2.2p95 (2015-04-13 revision 50295) [i686-linux]). qtbindings-4.8.6.2

Here is a simple program demonstrating the issue. The Ruby thread only runs when the mouse is moved over the Qt window, or a button is pressed etc. It never runs when the window is inactive. Firing the Qt:::Timer will allow the Ruby thread to run as expected. I ran into this problem trying to run an MQTT client (mqtt gem) in my Qt app, it has a thread which sends pings to keep it alive, but the ping thread does not run if the window is not being actively moused over, and gets disconnected eventually.

The Qt:; TImer workaround is acceptable, but it seems that as qtbindings says Ruby threads are fully supported maybe this should be addressed in the library itself.

require 'Qt'

app = Qt::Application.new(ARGV) do
  Qt::Widget.new do
    self.window_title = 'Bug test'
    resize(640,480)

    button = Qt::PushButton.new('Click me') do
      connect(SIGNAL :clicked) { puts "Clicked button" }
    end

   self.layout = Qt::VBoxLayout.new do
     addWidget(button)
    end

    show
  end

  # if the following is set to true then the Ruby thread runs
  if false
    # Hack to keep Ruby thread alive
    @keepAliveTimer = Qt::Timer.new( self ) do
      puts "hack timer"
    end

    @keepAliveTimer.start( 100 )
  end

  # this ruby thread does not run unless the mouse is moving over the window or a button is clicked etc
  Thread.new(Thread.current) do |parent|
       Thread.current[:parent] = parent
       while true do
         puts "thread running"
         sleep(1)
       end
  end

  exec
end
ghost commented 9 years ago

Calling exec within the Qt::Application constructor block doesn't allow the constructor to finish running and so the built in thread fix doesn't work. You just need to call exec on the application outside the block.

app = Qt::Application.new(ARGV) do
  Qt::Widget.new do
    self.window_title = 'Bug test'
    resize(640,480)

    button = Qt::PushButton.new('Click me') do
      connect(SIGNAL :clicked) { puts "Clicked button" }
    end

   self.layout = Qt::VBoxLayout.new do
     addWidget(button)
    end

    show
  end

  # if the following is set to true then the Ruby thread runs
  if false
    # Hack to keep Ruby thread alive
    @keepAliveTimer = Qt::Timer.new( self ) do
      puts "hack timer"
    end

    @keepAliveTimer.start( 100 )
  end

  # this ruby thread does not run unless the mouse is moving over the window or a button is clicked etc
  Thread.new(Thread.current) do |parent|
       Thread.current[:parent] = parent
       while true do
         puts "thread running"
         sleep(1)
       end
  end
  # Don't call exec here
end
app.exec
wolfmanjm commented 9 years ago

Ahh thank you , that makes sense.

FYI I copied it from here https://techbase.kde.org/Development/Languages/Ruby the example calls exec within the block. So maybe that example should be fixed ;)