From: Aamer Akhter Date: 2003-10-02T10:02:52+09:00 Subject: Re: tcltklib and not init'ing tk On 10/1/03 4:34 PM, in article 20031001203415.GB2632@vor-lord.ftc.agilent.com, "Brett H. Williams" wrote: > On Oct 2, Aamer Akhter wrote: >> >> Nice to see a fellow industry member dealing with the same problem ;-) >> >> On 10/1/03 10:06 AM, in article >> 20031001140652.GA29212@vor-lord.ftc.agilent.com, "Brett H. Williams" >> wrote: >> >>> On Oct 1, Aamer Akhter wrote: >>>> Hello, >>>> >>>> I'm using the tcltklib extension in ruby 1.8. It seems to be working >>>> well for my transition from tcl. There are a couple of stumbling >>>> blocks I'm running into: >>>> >>>> 1) is there a way to _not_ start tk? When I create a new interpreter >>>> (let's say from a non X-fwded ssh session) it may cause a failure. I'm >>>> only interested in Tcl. >>> >>> This is a big pain for me as well. I would really like to have this fixed, >>> as Ruby/Tcl interaction is important to me. But it hasn't been a big >>> enough pain to try and hack around in the C code :) (yet) >> >> I'm more than willing to spend some time on this. But I'm new to ruby, and >> to the tcl-C portions. >> >> It seems like the thing to do is be able to pass the desire for no tk in via >> the new call: >> >> TclTkIp.new(notk => 1), >> >> It looks like tcltklib.c:ip_init() gets called. The current parameters wind >> up being plugged into Tcl's argv0 and args. So, in keeping with the current >> implementation perhaps something like: >> >> TclTkIp.new($argv0, $args, 1) would be "better" >> >> Where 1 represents the non-desire to load Tk. >> >> One would need to skip over when 1 is set.: >> >> /* from Tcl_AppInit() */ >> DUMP1("Tk_Init"); >> if (Tk_Init(ptr->ip) == TCL_ERROR) { >> rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); >> } >> DUMP1("Tcl_StaticPackage(\"Tk\")"); >> #if TCL_MAJOR_VERSION >= 8 >> Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init, Tk_SafeInit); >> #else >> Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init, >> (Tcl_PackageInitProc *) NULL); >> #endif > > This kind of approach has merit... however, in the current setup, simply > doing: > > require 'tk' I think I've been using the library at a lower layer. This is where I get the problem: [20:28:02] chapanoke:~> irb irb(main):001:0> require 'tcltklib' => true irb(main):002:0> ip = TclTkIp.new("bobo") RuntimeError: this isn't a Tk applicationcouldn't connect to display "chapanoke:0.0" from (irb):2:in `initialize' from (irb):2:in `new' from (irb):2 I think 'tk' is probably doing a whole lot more than just loading tcltklib > > dies if the DISPLAY is not set. It seems a reorganization of how the > tcltklib module is presented would be needed as well. ;-) that may well be true. I'm just guessing on the above addition. I haven't tried anything yet. > > Am I missing a better way to get access to the Tcl interpreter? > >> ;-) yeah. I already ran into that. I've been using the ruby_fmt proc that >> tcltk creates on init. That's just a wrapper for calling the ruby tcl >> command. > > I have not been using this, but perhaps I should. The way I've been doing > it usually leads to naming differences and confusion, like: > > TclFuncs = <<-EOTCL > proc foo {args} { > ruby {bar('$args')} > } > EOTCL > > #TclFuncs is evaled into the tcl interpreter... > > def bar(args) > ... > end > > >>> You likely will have to do some creative things to get the scope that you >>> want--Tcl can only communicate directly with class or global variables. >>> This means if you call out to Tcl from Ruby, you will not have access to >>> instance or local variables. It makes perfect sense, but typically >>> requires doing some non-rubyish things to get access to what you need in >>> Tcl. >> >> I don't think the importance of the above has sunk in yet. Do you mean to >> say that in a class, I won't be able to do something like: >> >> Ip._eval("puts #{var}") > > No, because #{var} will be interpolated before the call to Tcl's eval. > > What I mean is that the following will not work (note that TclUtils > provides some wrapper functionality that ultimately results in a call to > Tk.tk_call("eval", cmd)... I didn't know about _eval which is probably what > I should be using): All roads eventually lead to _eval > > #tcl function defined as follows... > proc some_proc {} { > set somevar [ruby {do_something()}] > } > > #and a ruby methods defined as... > class SomeClass > def do_something() > ... > return some_str > end > > def some_instance_method() > TclUtils.eval("some_proc") > end > end > > When you call some_instance_method(), and it runs the tcl proc 'some_proc', > the Tcl interpreter is calling things at the basest level of Ruby scoping, > so there will be no such method 'do_something' at that level. Yes, that makes sense. And (given that you mentioned it) I see how it can get annoying pretty quick. > If this seemed obvious to you that such a thing wouldn't work, perhaps it > is. But it is an ugliness you have to deal with when spanning the two > worlds. > > I end up having a current_obj variable or something which can be gotten at > with a class method at the Tcl level, i.e.: > > proc some_proc {} { > set somevar [ruby {SomeClass.current_obj.do_something()}] > } > > #and... > class SomeClass > def SomeClass.current_obj > return @@current_obj > end > > ... > > def some_instance_method() > @@current_obj = self > TclUtils.eval("some_proc") > end > end > > Ugly. Sinfully ugly. Luckily, I try to keep Ruby in control of most > everything, and the Tcl is a configuration file, so I don't need to do this > that often.