📄 tk.rb
字号:
## tk.rb - Tk interface module using tcltklib# $Date: 2002/03/12 09:27:29 $# by Yukihiro Matsumoto <matz@netlab.co.jp># use Shigehiro's tcltklibrequire "tcltklib"require "tkutil"module TkComm WidgetClassNames = {} None = Object.new def None.to_s 'None' end Tk_CMDTBL = {} Tk_WINDOWS = {} def error_at frames = caller() frames.delete_if do |c| c =~ %r!/tk(|core|thcore|canvas|text|entry|scrollbox)\.rb:\d+! end frames end private :error_at def _genobj_for_tkwidget(path) return TkRoot.new if path == '.' begin tk_class = TkCore::INTERP._invoke('winfo', 'class', path) rescue return path end ruby_class = WidgetClassNames[tk_class] gen_class_name = ruby_class.name + 'GeneratedOnTk' unless Object.const_defined? gen_class_name eval "class #{gen_class_name}<#{ruby_class.name} def initialize(path) @path=path Tk_WINDOWS[@path] = self end end" end eval "#{gen_class_name}.new('#{path}')" end def tk_tcl2ruby(val) if val =~ /^rb_out (c\d+)/ return Tk_CMDTBL[$1] end if val.include? ?\s return val.split.collect{|v| tk_tcl2ruby(v)} end case val when /^@font/ TkFont.get_obj(val) when /^-?\d+$/ val.to_i when /^\./ Tk_WINDOWS[val] ? Tk_WINDOWS[val] : _genobj_for_tkwidget(val) when / / val.split.collect{|elt| tk_tcl2ruby(elt) } when /^-?\d+\.\d*$/ val.to_f else val end end def tk_split_list(str) return [] if str == "" idx = str.index('{') while idx and idx > 0 and str[idx-1] == ?\\ idx = str.index('{', idx+1) end return tk_tcl2ruby(str) unless idx list = tk_tcl2ruby(str[0,idx]) list = [] if list == "" str = str[idx+1..-1] i = -1 brace = 1 str.each_byte {|c| i += 1 brace += 1 if c == ?{ brace -= 1 if c == ?} break if brace == 0 } if str[0, i] == ' ' list.push ' ' else list.push tk_split_list(str[0, i]) end list += tk_split_list(str[i+1..-1]) list end def tk_split_simplelist(str) return [] if str == "" idx = str.index('{') while idx and idx > 0 and str[idx-1] == ?\\ idx = str.index('{', idx+1) end return str.split unless idx list = str[0,idx].split str = str[idx+1..-1] i = -1 brace = 1 str.each_byte {|c| i += 1 brace += 1 if c == ?{ brace -= 1 if c == ?} break if brace == 0 } if i == 0 list.push '' elsif str[0, i] == ' ' list.push ' ' else list.push str[0..i-1] end list += tk_split_simplelist(str[i+1..-1]) list end private :tk_tcl2ruby, :tk_split_list, :tk_split_simplelist def hash_kv(keys) conf = [] if keys and keys != None for k, v in keys conf.push("-#{k}") conf.push(v) end end conf end private :hash_kv def array2tk_list(ary) ary.collect{|e| if e.kind_of? Array "{#{array2tk_list(e)}}" elsif e.kind_of? Hash "{#{e.to_a.collect{|ee| array2tk_list(ee)}.join(' ')}}" else s = _get_eval_string(e) (s.index(/\s/))? "{#{s}}": s end }.join(" ") end private :array2tk_list def bool(val) case val when "1", 1, 'yes', 'true' TRUE else FALSE end end def number(val) case val when /^-?\d+$/ val.to_i when /^-?\d+\.\d*$/ val.to_f else val end end def string(val) if val == "{}" '' elsif val[0] == ?{ val[1..-2] else val end end def list(val) tk_split_list(val).to_a end def window(val) Tk_WINDOWS[val] end def procedure(val) if val =~ /^rb_out (c\d+)/ Tk_CMDTBL[$1] else nil end end private :bool, :number, :string, :list, :window, :procedure def _get_eval_string(str) return nil if str == None if str.kind_of?(String) # do nothing elsif str.kind_of?(Hash) str = hash_kv(str).join(" ") elsif str.kind_of?(Array) str = array2tk_list(str) elsif str.kind_of?(Proc) str = install_cmd(str) elsif str == nil str = "" elsif str == false str = "0" elsif str == true str = "1" elsif (str.respond_to?(:to_eval)) str = str.to_eval() else str = str.to_s() end return str end private :_get_eval_string def ruby2tcl(v) if v.kind_of?(Hash) v = hash_kv(v) v.flatten! v.collect{|e|ruby2tcl(e)} else _get_eval_string(v) end end private :ruby2tcl Tk_IDs = [0, 0] # [0]-cmdid, [1]-winid def _curr_cmd_id id = format("c%.4d", Tk_IDs[0]) end def _next_cmd_id id = _curr_cmd_id Tk_IDs[0] += 1 id end def install_cmd(cmd) return '' if cmd == '' id = _next_cmd_id Tk_CMDTBL[id] = cmd @cmdtbl = [] unless @cmdtbl @cmdtbl.push id return format("rb_out %s", id); end def uninstall_cmd(id) id = $1 if /rb_out (c\d+)/ =~ id Tk_CMDTBL[id] = nil end private :install_cmd, :uninstall_cmd def install_win(ppath) id = format("w%.4d", Tk_IDs[1]) Tk_IDs[1] += 1 if !ppath or ppath == "." @path = format(".%s", id); else @path = format("%s.%s", ppath, id) end Tk_WINDOWS[@path] = self end def uninstall_win() Tk_WINDOWS[@path] = nil end class Event def initialize(seq,a,b,c,d,f,h,k,m,o,p,s,t,w,x,y, aa,bb,dd,ee,kk,nn,rr,ss,tt,ww,xx,yy) @serial = seq @above = a @num = b @count = c @detail = d @focus = (f == 1) @height = h @keycode = k @mode = m @override = (o == 1) @place = p @state = s @time = t @width = w @x = x @y = y @char = aa @borderwidth = bb @wheel_delta = dd @send_event = (ee == 1) @keysym = kk @keysym_num = nn @rootwin_id = rr @subwindow = ss @type = tt @widget = ww @x_root = xx @y_root = yy end attr :serial attr :above attr :num attr :count attr :detail attr :focus attr :height attr :keycode attr :mode attr :override attr :place attr :state attr :time attr :width attr :x attr :y attr :char attr :borderwidth attr :wheel_delta attr :send_event attr :keysym attr :keysym_num attr :rootwin_id attr :subwindow attr :type attr :widget attr :x_root attr :y_root end def install_bind(cmd, args=nil) if args id = install_cmd(proc{|*arg| TkUtil.eval_cmd cmd, *arg }) id + " " + args else id = install_cmd(proc{|arg| TkUtil.eval_cmd cmd, Event.new(*arg) }) id + ' %# %a %b %c %d %f %h %k %m %o %p %s %t %w %x %y' + ' %A %B %D %E %K %N %R %S %T %W %X %Y' end end def tk_event_sequence(context) if context.kind_of? TkVirtualEvent context = context.path end if context.kind_of? Array context = context.collect{|ev| if ev.kind_of? TkVirtualEvent ev.path else ev end }.join("><") end if /,/ =~ context context = context.split(/\s*,\s*/).join("><") else context end end def _bind_core(mode, what, context, cmd, args=nil) id = install_bind(cmd, args) if cmd begin tk_call(*(what + ["<#{tk_event_sequence(context)}>", mode + id])) rescue uninstall_cmd(id) if cmd fail end end def _bind(what, context, cmd, args=nil) _bind_core('', what, context, cmd, args) end def _bind_append(what, context, cmd, args=nil) _bind_core('+', what, context, cmd, args) end def _bind_remove(what, context) tk_call(*(what + ["<#{tk_event_sequence(context)}>", ''])) end def _bindinfo(what, context=nil) if context tk_call(*what+["<#{tk_event_sequence(context)}>"]).collect {|cmdline| if cmdline =~ /^rb_out (c\d+)\s+(.*)$/ [Tk_CMDTBL[$1], $2] else cmdline end } else tk_split_simplelist(tk_call(*what)).collect!{|seq| l = seq.scan(/<*[^<>]+>*/).collect!{|subseq| case (subseq) when /^<<[^<>]+>>$/ TkVirtualEvent.getobj(subseq[1..-2]) when /^<[^<>]+>$/ subseq[1..-2] else subseq.split('') end }.flatten (l.size == 1) ? l[0] : l } end end private :install_bind, :tk_event_sequence, :_bind_core, :_bind, :_bind_append, :_bind_remove, :_bindinfo def bind(tagOrClass, context, cmd=Proc.new, args=nil) _bind(["bind", tagOrClass], context, cmd, args) end def bind_append(tagOrClass, context, cmd=Proc.new, args=nil) _bind_append(["bind", tagOrClass], context, cmd, args) end def bind_remove(tagOrClass, context) _bind_remove(['bind', tagOrClass], context) end def bindinfo(tagOrClass, context=nil) _bindinfo(['bind', tagOrClass], context) end def bind_all(context, cmd=Proc.new, args=nil) _bind(['bind', 'all'], context, cmd, args) end def bind_append_all(context, cmd=Proc.new, args=nil) _bind_append(['bind', 'all'], context, cmd, args) end def bindinfo_all(context=nil) _bindinfo(['bind', 'all'], context) end def pack(*args) TkPack.configure(*args) end def grid(*args) TkGrid.configure(*args) end def update(idle=nil) if idle tk_call 'update', 'idletasks' else tk_call 'update' end endendmodule TkCore include TkComm extend TkComm INTERP = TclTkIp.new INTERP._invoke("proc", "rb_out", "args", <<-'EOL') regsub -all {!} $args {\\!} args regsub -all "{" $args "\\{" args if {[set st [catch {ruby [format "TkCore.callback %%Q!%s!" $args]} ret]] != 0} { return -code $st $ret } { return $ret } EOL def callback_break fail TkCallbackBreak, "Tk callback returns 'break' status" end def callback_continue fail TkCallbackContinue, "Tk callback returns 'continue' status" end def after(ms, cmd=Proc.new) myid = _curr_cmd_id cmdid = install_cmd(cmd) tk_call("after",ms,cmdid)# return# if false #defined? Thread# Thread.start do# ms = Float(ms)/1000# ms = 10 if ms == 0# sleep ms/1000# cmd.call# end# else# cmdid = install_cmd(cmd)# tk_call("after",ms,cmdid)# end end def after_idle(cmd=Proc.new) myid = _curr_cmd_id cmdid = install_cmd(cmd) tk_call('after','idle',cmdid) end def clock_clicks(ms=nil) if ms tk_call('clock','clicks','-milliseconds').to_i else tk_call('clock','clicks').to_i end end def clock_format(clk, form=nil) if form tk_call('clock','format',clk,'-format',form).to_i else tk_call('clock','format',clk).to_i end end def clock_formatGMT(clk, form=nil) if form tk_call('clock','format',clk,'-format',form,'-gmt','1').to_i else tk_call('clock','format',clk,'-gmt','1').to_i end end def clock_scan(str, base=nil) if base tk_call('clock','scan',str,'-base',base).to_i else tk_call('clock','scan',str).to_i end end def clock_scanGMT(str, base=nil) if base tk_call('clock','scan',str,'-base',base,'-gmt','1').to_i else tk_call('clock','scan',str,'-gmt','1').to_i end end def clock_seconds tk_call('clock','seconds').to_i end def TkCore.callback(arg) arg = Array(tk_split_list(arg)) _get_eval_string(TkUtil.eval_cmd(Tk_CMDTBL[arg.shift], *arg)) end def scaling(scale=nil) if scale tk_call('tk', 'scaling', scale) else Float(number(tk_call('tk', 'scaling'))) end end def scaling_displayof(win, scale=nil) if scale tk_call('tk', 'scaling', '-displayof', win, scale) else Float(number(tk_call('tk', '-displayof', win, 'scaling'))) end end def appname(name=None) tk_call('tk', 'appname', name) end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -