Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / Using Tk with Gosu for the GUI?
- - By bigtunacan Date 2015-08-31 03:02
I'm trying to use Gosu to create a custom image editor for one of my games and I need some pretty heavy GUI needs in this case.  I thought using Tk would be an easier way to do the GUI piece, but I'm having issues with the application locking up whenever I try to use both Gosu and Tk in the same project.  I'm wondering if anyone else has done this and maybe could point me in the direction of some basic example code that could help me to get going in the right direction?
Parent - - By jlnr (dev) Date 2015-08-31 09:36
I've looked into several Ruby GUI toolkits (Tk, wx, Fox...), and they all seemed to be pretty obscure or half-broken on most platforms, so I haven't spent time integrating any of them with Gosu. The state of GUI programming in Ruby is terrible :|

Anyway, the biggest problem is that both Gosu and Tk (or any of the other UI/eventing libraries) have their own mainloop. Gosu's is here:

https://github.com/gosu/gosu/blob/master/src/Window.cpp#L179-L219

In theory I could unroll the while (true) loop and introduce a new method tick that handles a single step in the while loop, so that show becomes a simple while tick; end.
Then in Tk, you could just never call show, but keep the Gosu::Window running by calling tick regularly from Tk (a timer?). Do you feel like doing a little research work? I can push a beta gem.
Parent - - By bigtunacan Date 2015-08-31 11:44
I agree that most Ruby GUI toolkits have been left to rot, which is the sole reason I use Tk with Ruby since it is part of the standard library and I actually kept maintained (http://ruby-doc.org/stdlib-2.0.0/libdoc/tk/rdoc/).

If you can release a beta gem with the support you've described here I would love to give it spin.
Parent - - By jlnr (dev) Date 2015-08-31 15:25
Cool. I've pushed a --pre version of Gosu that has Window#tick as described above. Seems to work fine from a quick test, but I haven't integrated it with any libraries, only called tick in a while loop. It is still possible that Gosu's tick eats OS events that Tk is waiting for, or the other way around.

w = Gosu::Window.new(...)
loop { break if not w.tick }


Note that everything related to Gosu has to be called from the main thread.

Looking forward to your results :)
Parent - - By bigtunacan Date 2015-09-01 03:33
Thanks; that works great.

I put a code gist up of a hello world style sample that I tested it out with to get started; Posting it here in case someone else can make use of this as well.

https://gist.github.com/bigtunacan/99833dccbeab0359c796
Parent - - By jlnr (dev) Date 2015-09-01 08:13
Oh, Tk has an .update method? I thought it only had the full .mainloop. Do we even need Window#tick then? I've just created a TkRoot in a Window constructor and called .update on it from Window#update, and then just called window.show. The result seems to be the same as in your gist - a tiny Tk window alongside a working Gosu::Window.
Parent - - By bigtunacan Date 2015-09-01 09:25
Tk has the update method, but it's considered an anti-pattern to use it (Under certain cases, which I have not personally encountered, the Tk loop can get out of sync with events when using update directly).  The recommended mechanism is use the .after method that is used as a callback from mainloop.  So that is what I tried initially after your update.  It looked something like this.

def step(window, root)
  window.tick
  root.after(1000, step(window, root)
end

root = TkRoot.new { title "ex1" }
TkLabel.new(root) { text "hello world"; pack { padx 15; pady 15; side "left"; }; }
root.after(1000, step(window, root))
root.mainloop

The expected behavior there would be that Tk calls step once in approximately 1 second from now and then step adds itself back so that it will happen again.  But this was still causing some type of contention between Tk and Gosu and you end up with an unresponsive application.
Parent - By jlnr (dev) Date 2015-09-02 19:13
Ah, too bad. I'll take a look myself, but I'll put it on my lower-priority list if I can't figure it out quick enough.

It seems that using Tk's #update should be good enough for internal tools like level editors, though? That's actually good to know!
Parent - By jlnr (dev) Date 2015-09-13 10:52 Edited 2015-09-13 10:57
Wait, why do you call root.after(1000, step(window, root))? You need to pass the step as a proc/lambda, or it will be evaluated immediately.
(Why does after even accept two arguments? Mysteries of Ruby/Tk... :))

This works for me:

window = GameWindow.new
root = TkRoot.new { title "ex1" }
TkLabel.new(root) { text "hello world"; pack { padx 15; pady 15; side "left"; }; }
step = proc { window.tick; root.after(16, &step) }
step.call # start Gosu::Window#tick timer loop
root.mainloop


I think I'll leave Window#tick in the final Gosu 0.10.2, but mark it is experimental in the docs. Thanks for testing, and for nudging me to refactor Gosu a little more.
Up Topic Gosu / Gosu Exchange / Using Tk with Gosu for the GUI?

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill