Как обновить метку окна Ruby GTK в режиме реального времени и взаимодействовать с другими потоками

Я создаю приложение Ruby, которое состоит из кода, отвечающего за логику программы, и кода за графический интерфейс. Обе части кода разбиты на классы и выполняются в отдельных потоках.

Библиотека Ruby Gtk очень плохо документирована. Я хочу знать, как обновлять определенные элементы Gtk в режиме реального времени (например, текст в метке, которая находится в окне). Я хочу обновлять определенный элемент каждую секунду.

Я также хочу узнать, как потоки могут обмениваться данными. Я попытался использовать библиотеку Queue. Когда я использую его, я сталкиваюсь с ошибкой в ​​​​консоли:

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). Но выдает ошибку, что переменная не существует. Разве это не должно быть глобальным?


person Lukas Valatka    schedule 11.04.2015    source источник
comment
Документацию по Gtk обычно можно найти в ~/.gem/ruby/x.x.x/gems/gtk3-x.x.x/sample/. Вы можете опубликовать вопрос на странице проекта на github. github.com/ruby-gnome2/ruby-gnome2   -  person cedlemo    schedule 11.04.2015


Ответы (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
person cedlemo    schedule 11.04.2015
comment
Спасибо cedlemo за ответ. Я признаю, что это хорошая идея — передать классу ссылку на объект Queue. Однако при этом я столкнулся с другой проблемой: (( ruby ​​main.rb main.rb:14:in `initialize': not a Gtk::Window::Type: #‹Thread::Queue:0x9ea3c84› (TypeError)) Означает ли это, что класс принимает только аргументы определенного типа? Как я могу передать другой тип тогда? - person Lukas Valatka; 11.04.2015
comment
Для вашей ошибки попробуйте добавить круглые скобки после super -> super(). Посмотрите здесь: stackoverflow.com/ вопросы/5146974/ - person cedlemo; 11.04.2015
comment
Спасибо, это сработало! И все же, как мне обновить текст автоматически (без нажатия кнопки)? - person Lukas Valatka; 11.04.2015
comment
Говорите о суперпроблеме или о коде/редактировании, которое я добавил в свой первый ответ? - person cedlemo; 11.04.2015
comment
Нет, ваша идея добавить скобки к суперметоду сработала. Программа работает так, как ожидалось - при каждом нажатии кнопки метка автоматически обновляется текстом в объекте очереди. Теперь в дополнение я хочу реализовать метод, который бы автоматизировал процесс - например, автообновление или что-то в этом роде, чтобы пользователю не нужно было обновлять метку, нажав кнопку. - person Lukas Valatka; 11.04.2015
comment
Может быть, попробуйте упростить, как в коде, который я добавил. Не используйте очередь и обновляйте метку непосредственно в таймере. Посмотрите внимательно мой пример. - person cedlemo; 11.04.2015
comment
Но как мне заставить его обновляться каждый клик? Я пытался использовать цикл while, но он зависает в графическом интерфейсе. Взгляните на приведенный ниже код — Gtk.main должен быть выполнен, иначе GUI не появится. В классе окна невозможно реализовать какой-либо цикл. - person Lukas Valatka; 11.04.2015
comment
Давайте продолжим обсуждение в чате. - person cedlemo; 11.04.2015