I'm using Ubuntu 10.04 and I'm trying to run a WxRuby example which I copy-pasted from this site WxRubyWiki. I've been looking for help over the net but I couldn't find anything similar...
I'm getting this error...
Gtk:ERROR:/build/buildd/gtk+2.0-2.20.1/gtk/gtkwindow.c:6789:IA__gtk_window_present_with_time: assertion failed: (widget->window != NULL)
Aborted
These are the versions I'm using...
ruby -v
ruby 1.8.7 (2010-01-10 patchlevel 249) [i486-linux]
gem list
...
wxruby (2.0.1 x86-linux)
...
And this is the code I'm trying to run...
require 'rubygems' if RUBY_VERSION < '1.9'
require 'wx'
class EventFrame < Wx::Frame
def initialize()
super(nil, -1, "Event Frame")
#idleCounter = 0
evt_close {|event| on_close(event)}
evt_idle {|event| on_idle(event)}
evt_size {|event| on_size(event)}
evt_key_down {|event| on_key(event)}
evt_left_down {|event| on_left_down(event)}
# You can still process these events, you just need to define a separate callback for middle_down and right_down
# to process them as separate events
evt_middle_down {|event| on_middle_down(event)}
evt_right_down {|event| on_right_down(event)}
button = Wx::Button.new(self, -1, "Push me")
evt_button(button.get_id()) {|event| on_button(event)}
show()
end
def message(text, title)
m = Wx::MessageDialog.new(self, text, title, Wx::OK | Wx::ICON_INFORMATION)
m.show_modal()
end
def on_close(event)
message("This frame will be closed after you push ok", "Close event")
#close(true) - Don't call this - it will call on_close again, and your application will be caught in an infinite loop
# Either call event.skip() to allow the Frame to close, or call destroy(), as follows
destroy()
end
def on_idle(event)
#idleCounter += 1
if #idleCounter > 15 # Without the counter to slow this down, Idle events would be firing every second
message("The system is idle right now", "Idle event")
#idleCounter = 0
end
event.request_more() # You must include this, otherwise the Idle event won't occur again
end
def on_size(event)
size = event.get_size()
x = size.x
y = size.y
message("X = " + x.to_s + ", Y = " + y.to_s, "Size event")
end
def on_key(event)
message("Key pressed", "Key Event")
end
def on_left_down(event)
button = ""
if event.left_down()
button = "Left"
end
message(button + " button was clicked", "Mouse event")
end
def on_middle_down(event)
# This method hasn't been implemented yet...
#if event.middle_down()
#button = "Middle"
#end
message("Middle button was clicked", "Mouse event")
end
def on_right_down(event)
# This method hasn't been implemented yet...
#if event.right_down()
#button = "Right"
#end
message("Right button was clicked", "Mouse event")
end
def on_button(event)
message("Button was clicked", "Button event")
end
end
class MyApp < Wx::App
def on_init
EventFrame.new()
end
end
MyApp.new.main_loop
Thanks in advance!
It is more of an understanding of how GTK+2/wx/wxRuby works. As is, the code above does not work on your configuration in a Virtual Box machine I set to test this nor my development machine's Ubuntu 11.10 x86_64 with 1000Hz kernel compile option and ruby 1.9.3 p21.
The GTK+2 error occurs when the on_size event fires during frame creation. The creation is not yet complete so the message box does not have a parent at that time. You can test this by commenting out (in def on_size(event):
message("X = " + x.to_s + ", Y = " + y.to_s, "Size event")
and trying:
puts "Size event: X = #{x}, Y = #{y}"
to see the event details on standard out. You will notice there are two events fired during creation; an initial size event and a resize event
Another caveat is the idle loop which locks Unity o my system. You can test that the idle event is happening without a lock up by changing the code as below:
in def initialize add this before show():
create_status_bar(2)
self.status_text = "Welcome to wxRuby!"
then in idle_event:
def on_idle(event)
#idleCounter += 1
#if #idleCounter > 15 # Without the counter to slow this down, Idle events would be firing every second
# message("The system is idle right now", "Idle event")
# #idleCounter = 0
#end
set_status_text #idleCounter.to_s, 1
event.request_more() # You must include this, otherwise the Idle event won't occur again
end
A last caveat about the code is you may notice you don't see the key or mouse down events creating their message box. That is because the button control fills the client area and traps the frame events for keys and buttons. If you resize the frame while the app is running the button will not resize with it by default (GTK+2 platform). If you then click inside the frame client area but not on the button you will see mouse events.
Good Luck !
this fixed and works in ubuntu 11.10 :)
Related
I have written the program below. It displays an image within a hbox inside a scrolledwindow. The image moves forward when space key is pressed, backwards when backspace is pressed. Whenever an image is changed, I set the scrolledwindow adjustments value to 0.0 so that it will always display from the start. But this is not working correctly, for example when I use the mouse wheel to scroll the image up or down, press space or backspace, it will sometimes show the next/previous image from the top or stay at that position. I want it to always show the next/prev image from the top. Have I messed up somewhere? I'm using ruby-gtk 4.0.8.
require 'gtk3'
class Window < Gtk::ApplicationWindow
def initialize(app)
super(app)
set_default_size(800, 600)
path = File.expand_path('~/Manga/Deadman Wonderland')
#images = Dir.children(path).map { |file| "#{path}/#{file}" }
#sc_win = Gtk::ScrolledWindow.new
#box = Gtk::Box.new(:horizontal)
#box.halign = :center
#box.valign = :center
#image = Gtk::Image.new
#image_index = -1
signal_connect('key-press-event') do |_widg, event|
case event.keyval
when Gdk::Keyval::KEY_space
reset_viewport
next_image
when Gdk::Keyval::KEY_BackSpace
reset_viewport
prev_image
end
end
next_image
#box.add(#image)
#sc_win.add(#box)
add(#sc_win)
end
def next_image
if #image_index < #images.length - 1
#image.pixbuf.unref if #image.pixbuf
#image_index += 1
#image.set_pixbuf(GdkPixbuf::Pixbuf.new(:file => #images[#image_index]))
end
end
def prev_image
if #image_index > 0
#image.pixbuf.unref if #image.pixbuf
#image_index -= 1
#image.set_pixbuf(GdkPixbuf::Pixbuf.new(:file => #images[#image_index]))
end
end
def reset_viewport
#sc_win.hadjustment.set_value(0.0)
#sc_win.vadjustment.set_value(0.0)
end
end
app = Gtk::Application.new(nil, :flags_none)
app.signal_connect('activate') { Window.new(app).show_all }
app.run
I tried printing out the value of vertical adjustment whenever it changed. I noticed that at some point after calling reset_viewport, its value reset from 0.0 to the previous one.
#sc_win.vadjustment.signal_connect('value-changed') do
p #sc_win.vadjustment.value
end
494.0 #at bottom of image
0.0 #after pressing space key
494.0 #jumps back!
This is my code:
require "gtk3"
builder_file = "#{File.expand_path(File.dirname(__FILE__))}/example.ui"
builder = Gtk::Builder.new(:file => builder_file)
window = builder.get_object("window")
window.signal_connect("destroy") { Gtk.main_quit }
progressbar=builder.get_object("progressbar1")
progressbar.set_fraction(0.5)
button = builder.get_object("button1")
button.signal_connect("clicked") {
for i in(0..4)
sleep(1)
puts "query " + (i+1).to_s + " done"
a=(i+1)/5.to_f
puts a
progressbar.set_fraction(a)
end
}
Gtk.main
sleep is just a placeholder for a web query that takes about 1 second. When I execute this code on my machine, the console output is fine, but the progress bar stays empty until it jumps to full after five seconds, which is not what I want. How can I make use of the progress bar?
The integration of the event loop of the GUI tool (Gtk, Tk, etc) and Ruby annoys beginners including me. I know two ways.
1.add thread
require 'gtk3'
win = Gtk::Window.new('Sample')
win.signal_connect('destroy') { Gtk.main_quit }
box = Gtk::Box.new(:vertical)
pb = Gtk::ProgressBar.new
pb.set_fraction(0.5)
b = Gtk::Button.new(label: 'Button')
b.signal_connect('clicked') do
Thread.new do
5.times do |i|
sleep 1
pb.fraction = (i + 1) / 5.0
end
end
end
win.add box
box.pack_start pb
box.pack_start b
win.show_all
Gtk.main
2.use GLib::Timeout
b.signal_connect('clicked') do
i = 0.0
GLib::Timeout.add(1000) do |a|
i += 0.2
pb.fraction = i
i < 1.0 # return false and stop when i >= 1.0
end
end
I'm pretty new to ruby and am trying to implement a Tk application that will display a window prompting for input at a certain interval. In between the interval I want the window to not display in any taskbars, etc. and so I've implemented the following code that seems to work perfectly the first time through, but after the window displays the second time and I enter text in the TkEntry and click the TkButton the window is dismissed and never returns. I've tried putting in some "puts" calls at key locations to see what is happening and it seems that it never even makes it past the call to "displayUi".
*EDIT:
I'm running ruby 1.9.3p385 (2013-02-06) [i386-mingw32] on a Windows 7 system (in case that makes any difference)
Any help (even if it's providing a different mechanism to accomplish the same goal) would be appreciated, but please keep in mind that I'm a ruby noobie. Thanks!
require "tk"
class Sample
attr_accessor :root, :active
#active = false
def initialize
# init
end
def entry (task)
# do some work here
#active = false
end
def displayUi ()
#active = true
if (#root.nil?)
#root = TkRoot.new { title "Sample App" }
else
# already running just restart
Tk.restart
end
TkLabel.new(#root) {
text 'Sample Text'
pack { padx 15; pady 15; side 'left' }
}
statusInput = TkEntry.new(#root) {
pack('side'=>'left', 'padx'=>10, 'pady'=>10)
}
statusInput.focus
response = TkVariable.new
statusInput.textvariable = response
TkButton.new(#root, :text => "Ok", :command => proc { entry(response.value); #root.destroy }) {
pack('side'=>'left', 'padx'=>10, 'pady'=>10)
}
Tk.mainloop
end
end
i=0
st = Sample.new
while (true)
if (!st.active)
st.displayUi()
end
sleep(1)
end
In ruby, is it possible to cause a thread to pause from a different concurrently running thread.
Below is the code that I've written so far. I want the user to be able to type 'pause thread' and the sample500 thread to pause.
#!/usr/bin/env ruby
# Creates a new thread executes the block every intervalSec for durationSec.
def DoEvery(thread, intervalSec, durationSec)
thread = Thread.new do
start = Time.now
timeTakenToComplete = 0
loopCounter = 0
while(timeTakenToComplete < durationSec && loopCounter += 1)
yield
finish = Time.now
timeTakenToComplete = finish - start
sleep(intervalSec*loopCounter - timeTakenToComplete)
end
end
end
# User input loop.
exit = nil
while(!exit)
userInput = gets
case userInput
when "start thread\n"
sample500 = Thread
beginTime = Time.now
DoEvery(sample500, 0.5, 30) {File.open('abc', 'a') {|file| file.write("a\n")}}
when "pause thread\n"
sample500.stop
when "resume thread"
sample500.run
when "exit\n"
exit = TRUE
end
end
Passing Thread object as argument to DoEvery function makes no sense because you immediately overwrite it with Thread.new, check out this modified version:
def DoEvery(intervalSec, durationSec)
thread = Thread.new do
start = Time.now
Thread.current["stop"] = false
timeTakenToComplete = 0
loopCounter = 0
while(timeTakenToComplete < durationSec && loopCounter += 1)
if Thread.current["stop"]
Thread.current["stop"] = false
puts "paused"
Thread.stop
end
yield
finish = Time.now
timeTakenToComplete = finish - start
sleep(intervalSec*loopCounter - timeTakenToComplete)
end
end
thread
end
# User input loop.
exit = nil
while(!exit)
userInput = gets
case userInput
when "start thread\n"
sample500 = DoEvery(0.5, 30) {File.open('abc', 'a') {|file| file.write("a\n")} }
when "pause thread\n"
sample500["stop"] = true
when "resume thread\n"
sample500.run
when "exit\n"
exit = TRUE
end
end
Here DoEvery returns new thread object. Also note that Thread.stop called inside running thread, you can't directly stop one thread from another because it is not safe.
You may be able to better able to accomplish what you are attempting using Ruby Fiber object, and likely achieve better efficiency on the running system.
Fibers are primitives for implementing light weight cooperative
concurrency in Ruby. Basically they are a means of creating code
blocks that can be paused and resumed, much like threads. The main
difference is that they are never preempted and that the scheduling
must be done by the programmer and not the VM.
Keeping in mind the current implementation of MRI Ruby does not offer any concurrent running threads and the best you are able to accomplish is a green threaded program, the following is a nice example:
require "fiber"
f1 = Fiber.new { |f2| f2.resume Fiber.current; while true; puts "A"; f2.transfer; end }
f2 = Fiber.new { |f1| f1.transfer; while true; puts "B"; f1.transfer; end }
f1.resume f2 # =>
# A
# B
# A
# B
# .
# .
# .
Say I have the following sort of app:
Shoes.app do
#i = 0
def add_button
button ("#{#i += 1}")
end
button("0") {add_button}
end
So that each time you click the button it adds a new button with a higher number. Is there any way to code it so that clicking one of the new buttons displays its number? Since self always points to the app, the obvious approach
button ("#{#i += 1}") {alert #i}
Doesn't work, since then clicking any button just displays the current value of #i.
Shoes.app do
#i = 0
def add_button
n = #i+1
button ("#{#i += 1}") {alert n}
end
button("0") {add_button}
end