如何实时更新Ruby GTK window标签并与其他线程通信

How to update Ruby GTK window label in real time and communicate with other threads

我正在构建一个 Ruby 应用程序,其中包含负责程序逻辑的代码和负责 GUI 的代码。代码的两部分在 classes 和 运行 中拆分在不同的线程中。

Ruby Gtk 库的文档非常少。 我想知道如何实时更新特定的 Gtk 元素(例如,标签中的文本位于 window 中)。我想每秒更新一个特定的元素。

我也想知道线程如何交换数据。我试过使用队列库。当我使用它时,我 运行 在控制台中出现错误:

undefined local variable or method `queue' for #<TimerWindow:0xa36d634 ptr=0xb4201178>

程序代码:

require_relative 'notifications'
require_relative 'settings_reader'


require 'gtk3'
require "thread"

q = Queue.new

class TimerWindow < Gtk::Window

  def initialize
    @label = ""

        super
        init_ui
  end

    def init_ui

    fixed = Gtk::Fixed.new
    add fixed

    button = Gtk::Button.new :label => "Quit"
        button.set_size_request 80, 35      
        button.signal_connect "clicked" do 
      @label.set_text q.pop
        end

    fixed.put button, 50, 50

        set_title  "Tomatono"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        set_border_width 10
        @label = Gtk::Label.new "HEY"
        fixed.put @label, 20, 20


        set_default_size 250, 200
        set_window_position :center



        show_all
    end

end


class Timer

  def initialize
    # Current time in seconds
    @time = 0

    settings = Settings_Reader.new
    @work_time = Integer(settings.work_time) * 60
    @break_time = Integer(settings.break_time) * 60
    @work_text = settings.work_text
    @return_text = settings.return_text
    @break_text = settings.break_text
    @work_notif_header = settings.work_notif_header
    @break_notif_header = settings.break_notif_header
    @status_notif_header = settings.status_notif_header
    @work_status = settings.work_status
    @break_status = settings.break_status
  end

  def launch

    while true
      work_time()
      break_time()
    end

  end

  def work_time()
    puts @work_text

    notification = Notif.new(@work_notif_header, @work_text)
    notification.post

    @time = 0
    sleep(1)

      while @time < @work_time
    @time += 1
    puts "#{min_remaining()} minutes remaining" if (@time % 60) == 0

    if (@time % 60) == 0
      notification = Notif.new(@work_notif_header, "#{@work_status} #{@time / 60} minutes.")
      notification.post
    end
    q << @time
    sleep(1)
      end

  end

  def break_time
    puts @break_text
    @time = 0
    sleep(1)

    while @time < @break_time
    @time += 1
    puts "#{min_remaining()} minutes remaining" if (@time % 60) == 0
    notification = Notif.new(@break_notif_header, "#{@break_status} #{@time / 60} minutes.")
    notification.post

    q << @time
    sleep(1)
    end

  end

  def reset
  end

  def stop_time
  end

  def min_remaining()
    (1500 - @time) / 60
  end


end



app = Thread.new {
  timer = Timer.new
  timer.launch
}

gui = Thread.new {
  Gtk.init
    window = TimerWindow.new
    #window.update
  Gtk.main
}

app.join
gui.join

每当我按下 "Quit" 按钮时,我希望标签文本更改为 q 变量中设置的值,在定时器 class 中设置(在 while 循环中)。但是它抛出一个变量不存在的错误。它不应该是全球性的吗?

否 是局部变量:

myglobal = "toto"

class Myclass
  def initialize
    @myvar = myglobal
  end
  def print
    puts @myvar
  end
end

an_instance = Myclass.new

an_instance.print

抛出此错误:

global_and_class.rb:5:in `initialize': undefined local variable or method `myglobal' for #<Myclass:0x00000000a74ce8> (NameError)

但如果您将 myglobal 指定为全局变量,它会起作用:

$myglobal = "toto"

class Myclass
  def initialize
    @myvar = $myglobal
  end
  def print
    puts @myvar
  end
end

an_instance = Myclass.new

an_instance.print

但是你应该小心使用全局变量。为什么不使用 Queue 实例作为初始化方法的参数?

** 编辑 **

首先,这是一个仅使用局部变量的简单示例:

#!/usr/bin/env ruby

require "gtk3"
label = Gtk::Label.new("test")
othert = Thread.new {
  loop { 
    puts 'thread running';
     label.text = Time.now.to_s; sleep 1 }
}
maint = Thread.new {
  win = Gtk::Window.new
  win.set_default_size 100, 30
  win.add(label)

  win.show_all
  win.signal_connect("destroy") {othert.kill;Gtk.main_quit}
  Gtk.main
}
maint.join
othert.join

或许你应该从这里开始,看看如何造就你类。

编辑 2

class TimerWindow < Gtk::Window
  def initialize(label)
    super()
    add(label)
  end
end

alabel = Gtk::Label.enw("test")
othert = Thread.new {
  loop { 
    puts 'thread running';
     label.text = Time.now.to_s; sleep 1 }
}
maint = Thread.new {
  win = TimerWindow.new(alabel)
  win.set_default_size 100, 30
  win.show_all
  win.signal_connect("destroy") {othert.kill;Gtk.main_quit}
  Gtk.main
}
maint.join
othert.join