【问题标题】:How to update Ruby GTK window label in real time and communicate with other threads如何实时更新Ruby GTK窗口标签并与其他线程通信
【发布时间】:2015-04-11 14:28:38
【问题描述】:

我正在构建一个 Ruby 应用程序,它由负责程序逻辑的代码和用于 GUI 的代码组成。代码的两部分都拆分为类并在单独的线程中运行。

Ruby Gtk 库的文档记录很差。 我想知道如何实时更新特定的 Gtk 元素(例如,标签中的文本,在窗口中)。我想每秒更新一个特定元素。

我也想了解线程如何交换数据。我试过使用队列库。使用的时候,控制台报错:

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

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

【问题讨论】:

  • 对于 Gtk 的文档,您通常可以在 ~/.gem/ruby/x.x.x/gems/gtk3-x.x.x/sample/ 中找到示例。您可以在项目的 github 页面上发布问题。 github.com/ruby-gnome2/ruby-gnome2

标签: ruby multithreading user-interface gtk graphical-programming


【解决方案1】:

否,它是一个局部变量:

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

【讨论】:

  • 感谢 cedlemo 的回答。我承认将 Queue 对象的引用传递给类是个好主意。然而这样做我遇到了另一个问题:((ruby main.rb main.rb:14:in `initialize': not a Gtk::Window::Type: #<:queue:0x9ea3c84> (TypeError)))这是否意味着该类只接受特定类型的参数?那我怎样才能传递另一种类型呢?
  • 对于你的错误尝试在 super -> super() 之后添加括号。看这里:stackoverflow.com/questions/5146974/…
  • 谢谢,成功了!不过,我怎样才能自动更新文本(无需按下按钮)?
  • 是在谈论超级问题还是我在第一个答案中添加的代码/编辑?
  • 不,您在超级方法中添加括号的想法确实有效。该程序按预期工作 - 每当按下按钮时,标签会自动使用队列对象中的文本进行自我更新。现在,我还想实现一个方法来自动化这个过程——比如自动更新或其他东西,这样用户就不需要通过点击按钮来刷新标签。
猜你喜欢
  • 1970-01-01
  • 2016-01-02
  • 1970-01-01
  • 1970-01-01
  • 2012-04-20
  • 1970-01-01
  • 2018-11-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多