Murga-Projects Forums

Full Version: Exploring tables
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
While exploring the built-in tables in MurgaLua, I discovered that there are several parts of FLTK that are not documented...classes, methods, constants, and functions. So I whipped up this tool to let me quickly run down through various tables, starting with _G.

It needs work, since it does occasionally crash, and it doesn't really do anything other than display the contents of a selected table (tables are displayed in bold font).

Code:
#!/usr/bin/murgaLua-0.5.5

-- Change the default box
Fl:set_boxtype(fltk.FL_UP_BOX,fltk.FL_THIN_UP_BOX)
Fl:set_boxtype(fltk.FL_DOWN_BOX,fltk.FL_THIN_DOWN_BOX)

function lookittable()
  outpoot:clear()
  table.sort(current_table)
  --for k,v in pairs(current_table) do outpoot:add(tostring(k).."\t\t"..tostring(v)) end
  for k,v in pairs(current_table) do
      if type(v)=="table" then
          outpoot:add("@b"..tostring(k))
      else
          outpoot:add(tostring(k))
      end
end
end

ww=400
wh=360

w=fltk:Fl_Double_Window(ww,wh,"view murga tables")

outpoot=fltk:Fl_Hold_Browser(10,40,ww-20,wh-50)
--outpoot:column_widths((ww-20)/2) -- not supported?
--outpoot:textfont(fltk.FL_COURIER)

butt=fltk:Fl_Button(10,10,80,30,"view table")
butt:callback(
function()
  if outpoot:value() > 0 and type(current_table) == "table" then
    for k,v in pairs(current_table) do
      --if string.find(outpoot:text(outpoot:value()),"^"..tostring(k).."%s") and type(v) == "table" then
      if outpoot:text(outpoot:value()) == "@b"..tostring(k) then
    current_table=v
    title:label(tostring(k))
        lookittable()
    break
      end
    end
  end
end
)

title=fltk:Fl_Box(90,10,ww-180,30)
title:box(fltk.FL_THIN_UP_BOX)

resetbutt=fltk:Fl_Button(ww-90,10,80,30,"restart")
resetbutt:callback(
function()
title:label("_G")
current_table=_G
lookittable()
end
)

current_table=_G
lookittable()
w:show()
Fl:run()

[quote=mikshaw]
While exploring the built-in tables in MurgaLua, I discovered that there are several parts of FLTK that are not documented...classes, methods, constants, and functions. So I whipped up this tool to let me quickly run down through various tables, starting with _G.
[/quote=mikshaw]
Cool graphic tool ...


This is the way I explored the Lego NXT pbLua rocks :-)

Christian
A few changes now:
Syntax highlighting according to the object type.
You don't need to click the button anymore (it's still there because I have other plans for it).
Functions now give a little bit of info (not much, but it's all I know how to get at this time).
Resizable window

TODO:
Navigate backward rather than having to start back at _G every time.
Try to figure out how to provide more info about selected functions.
Maybe try to do some column layout (multiple browsers?)

Code:
#!/usr/bin/murgaLua

Fl:set_boxtype(fltk.FL_UP_BOX,fltk.FL_THIN_UP_BOX)
Fl:set_boxtype(fltk.FL_DOWN_BOX,fltk.FL_THIN_DOWN_BOX)

function lookittable()
  outpoot:clear()
  table.sort(current_table)
  for k,v in pairs(current_table) do
    if type(v)=="table" then
      outpoot:add("@b"..tostring(k))
    elseif type(v)=="function" then
      outpoot:add("@C1"..tostring(k).." (function)")
    elseif type(v)=="number" then
      outpoot:add("@C5"..tostring(k).." (number)\t"..tostring(v))
    elseif type(v)=="userdata" then
      outpoot:add("@C9"..tostring(k).." (userdata)\t\t"..tostring(v))
    elseif type(v)=="string" then
      outpoot:add("@C4"..tostring(k).." (string)\t\t"..tostring(v))
    else
      outpoot:add(tostring(k))
    end
  end
end

function which_is_it()
  local this_text=outpoot:text(outpoot:value())
  if outpoot:value() > 0 and type(current_table) == "table" then
    for k,v in pairs(current_table) do
      if this_text == "@b"..tostring(k) then
        current_table=v
        title:label(tostring(k))
        lookittable()
        break
      end
      if string.find(this_text,tostring(k).."%s%(function%)$") then
        current_table=debug.getinfo(v)
        title:label(tostring(k))
        lookittable()
        break
      end
    end
  end
end

ww=400
wh=360

w=fltk:Fl_Double_Window(ww,wh,"view murga tables")

outpoot=fltk:Fl_Hold_Browser(10,40,ww-20,wh-50)
outpoot:callback(which_is_it)

butt=fltk:Fl_Button(10,10,80,30,"view table")
butt:callback(which_is_it)

title=fltk:Fl_Box(90,10,ww-180,30)
title:box(fltk.FL_THIN_UP_BOX)

resetbutt=fltk:Fl_Button(ww-90,10,80,30,"restart")
resetbutt:callback(
function()
  title:label("_G")
  current_table=_G
  lookittable()
end
)

w:resizable(outpoot)
current_table=_G
lookittable()
w:show()
Fl:run()

New changes:
Added a "back" button to make navigation more rational
Much better sorting of the table: rather than sorting the table itself (which doesn't really work with string indexes) it uses a temporary table to presort the strings that will eventually go into the browser.

I haven't had a crash with it lately, so that must have been fixed =o)

I still want to try to figure out if I can get more information about selected functions, but so far it doesn't look promising. Ideally I would like to be able to display the contents of the functions, which I think should be possible at least with the Lua (non-C) functions.

I tried adding "thread" and "boolean" to the syntax highlighting, but there seems to be an issue when separating those two types (only one or the other displays). I'll need to look into that in the future.

Code:
#!/usr/bin/murgaLua

Fl:set_boxtype(fltk.FL_UP_BOX,fltk.FL_THIN_UP_BOX)
Fl:set_boxtype(fltk.FL_DOWN_BOX,fltk.FL_THIN_DOWN_BOX)

my_depth=0
my_level={}
my_title={}
my_line={}

-- text colors
col_tab="@b"
col_func="@C1"
col_str="@C4"
col_num="@C13"
col_usr="@C8@B17"
--col_thr="@C9"
--col_bool="@C5"

function lookittable()
  outpoot:clear()
  local my_value,my_string
  local tmptab={} -- temporary table for alphabetizing the list of my_string's
  for k,v in pairs(current_table) do
    my_value=tostring(k).."\t\t"..tostring(v)
    if     type(v)=="table" then my_string=col_tab..tostring(k)
    elseif type(v)=="function" then my_string=col_func..my_value
    elseif type(v)=="number" then my_string=col_num..my_value
    elseif type(v)=="userdata" then my_string=col_usr..my_value
    elseif type(v)=="string" then my_string=col_str..my_value
    --elseif type(v)=="thread" then my_string=col_thr..my_value
    --elseif type(v)=="boolean" then my_string=col_bool..my_value
    else my_string=tostring(k)
    end
    table.insert(tmptab,my_string)
  end
  table.sort(tmptab) -- alphabetize
  for i,v in ipairs(tmptab) do outpoot:add(v) end -- add the sorted list to the browser
  title:label(my_title[my_depth])
  if my_depth==0 then backbutt:deactivate() else backbutt:activate() end
  outpoot:middleline(my_line[my_depth])
end

function move_forward(k,v)
  my_depth=my_depth+1 -- move up a level
  my_level[my_depth]=current_table
  my_title[my_depth]=tostring(k).." ("..type(v)..")"
  my_line[my_depth]=1
  lookittable()
end

function move_back()
if my_depth > 0 then
  current_table=my_level[my_depth-1]
  my_depth=my_depth-1
  lookittable()
end
end

function which_is_it()
  local this_text=outpoot:text(outpoot:value())
  if outpoot:value() > 0 and type(current_table) == "table" then
    my_line[my_depth]=outpoot:value()
    -- Compare the browser text with each key in current_table until a match is found
    -- This could probably be streamlined
    for k,v in pairs(current_table) do
      -- it's a table
      if this_text == col_tab..tostring(k) then
        my_level[my_depth]=current_table --set a table to return to with "back"
        current_table=v
    move_forward(k,v)
        break
      end
      -- it's a function
      if string.find(this_text,"^"..col_func..tostring(k)) then
        -- is there a way to get more useful info?
        my_level[my_depth]=current_table
        current_table=debug.getinfo(v,"nSlufL")
    move_forward(k,v)
        break
      end
    end
  end
end

ww=400
wh=360

w=fltk:Fl_Double_Window(ww,wh,"view murga tables")

outpoot=fltk:Fl_Select_Browser(10,40,ww-20,wh-50)
--outpoot:column_widths((ww-20)/2) -- not supported?
outpoot:selection_color(0)
outpoot:callback(which_is_it)

--[[
butt=fltk:Fl_Button(ww-90,10,80,30,"view table")
butt:callback(which_is_it)
butt:deactivate()
]]

title=fltk:Fl_Box(90,10,ww-100,30)
title:box(fltk.FL_THIN_UP_BOX)

backbutt=fltk:Fl_Button(10,10,80,30,"@<- back")
backbutt:callback(move_back)

w:resizable(outpoot)
current_table=_G
my_title[my_depth]="Global Environment (table: _G)"
my_line[my_depth]=1
lookittable()
w:show()
Fl:run()

Not meaning to toot, but this tool has provided me with some information over the last few days that I would otherwise have had no idea about.

Some of the FLTK classes are not available in murgaLua with the typical syntax of fltk:Fl_* or fltk.Fl_*
Instead they are supplied as tables on the top level.
For example, I had for the longest time assumed that Fl_Tooltip was not availabe in murgaLua because I kept getting an error stating that fltk.Fl_Tooltip was a nil value. Now that I look at the tables in _G I see that Fl_Tooltip is available, but just not in the fltk table.

There are at least a few things like this, and additional classes that aren't covered in the FLTK docs.
Made a couple of changes based on Bill's feedback.
The which_is_it function no longer crashes, although the information it displays seems to be incorrect. I'll need to work on that more later.
Multiline strings are now displayed on a singe line.
Clicking a userdata entry will display its metatable.

Code:
Fl:set_boxtype(fltk.FL_UP_BOX,fltk.FL_THIN_UP_BOX)
Fl:set_boxtype(fltk.FL_DOWN_BOX,fltk.FL_THIN_DOWN_BOX)

my_depth=0
my_level={}
my_title={}
my_line={}

-- text colors
col_tab="@b"
col_func="@C1"
col_str="@C4"
col_num="@C13"
col_usr="@C8@B17"
--col_thr="@C9"
--col_bool="@C5"

function lookittable()
  outpoot:clear()
  local my_value,my_string
  local tmptab={} -- temporary table for alphabetizing the list of my_string's
  for k,v in pairs(current_table) do
    my_value=tostring(k).."\t\t"..tostring(v)
    if     type(v)=="table" then my_string=col_tab..tostring(k)
    elseif type(v)=="function" then my_string=col_func..my_value
    elseif type(v)=="number" then my_string=col_num..my_value
    elseif type(v)=="userdata" then my_string=col_usr..my_value
    elseif type(v)=="string" then my_string=col_str..string.gsub(my_value,"\n"," %* ")
    --elseif type(v)=="thread" then my_string=col_thr..my_value
    --elseif type(v)=="boolean" then my_string=col_bool..my_value
    else my_string=tostring(k)
    end
    table.insert(tmptab,my_string)
  end
  table.sort(tmptab) -- alphabetize
  for i,v in ipairs(tmptab) do outpoot:add(v) end -- add the sorted list to the browser
  title:label(my_title[my_depth])
  if my_depth==0 then backbutt:deactivate() else backbutt:activate() end
  outpoot:middleline(my_line[my_depth])
end

function move_forward(k,v)
  my_depth=my_depth+1 -- move up a level
  my_level[my_depth]=current_table
  my_title[my_depth]=tostring(k).." ("..type(v)..")"
  my_line[my_depth]=1
  lookittable()
end

function move_back()
if my_depth > 0 then
  current_table=my_level[my_depth-1]
  my_depth=my_depth-1
  lookittable()
end
end

function which_is_it()
  local this_text=outpoot:text(outpoot:value())
  if outpoot:value() > 0 and type(current_table) == "table" then
    my_line[my_depth]=outpoot:value()
    -- Compare the browser text with each key in current_table until a match is found
    -- This could probably be streamlined
    for k,v in pairs(current_table) do
      -- it's a table
      if this_text == col_tab..tostring(k) then
        my_level[my_depth]=current_table --set a table to return to with "back"
        current_table=v
    move_forward(k,v)
        break
      end
--[[
      -- it's a string
      if string.find(this_text,"^"..col_str..tostring(k)) then
        print(v)
        break
      end
]]
      -- it's userdata
      if string.find(this_text,"^"..col_usr..tostring(k)) then
        my_level[my_depth]=current_table
        current_table=getmetatable(v)
    move_forward(k,v)
        break
      end
      -- it's a function
      if string.find(this_text,"^"..col_func..tostring(k))  then
        -- is there a way to get more useful info?
        my_level[my_depth]=current_table
        -- this test fixes a crash, but doesn't supply the right info
        if string.find(this_text,"which_is_it") then getinfo_level=1 else getinfo_level=v end
        current_table=debug.getinfo(getinfo_level,"nSlufL")
    move_forward(k,v)
        break
      end
    end
  end
end

ww=400
wh=360

w=fltk:Fl_Double_Window(ww,wh,"view murga tables")

outpoot=fltk:Fl_Select_Browser(10,40,ww-20,wh-50)
--outpoot:column_widths((ww-20)/2) -- not supported?
outpoot:selection_color(0)
outpoot:callback(which_is_it)

title=fltk:Fl_Box(90,10,ww-100,30)
title:box(fltk.FL_THIN_UP_BOX)

backbutt=fltk:Fl_Button(10,10,80,30,"@<- back")
backbutt:callback(move_back)

w:resizable(outpoot)
current_table=_G
my_title[my_depth]="Global Environment (table: _G)"
my_line[my_depth]=1
lookittable()
w:show()
Fl:run()

Making everything local keeps the stuff created in this script from appearing in _G, so I guess this version should show only what is provided in murgaLua.

Code:
Fl:set_boxtype(fltk.FL_UP_BOX,fltk.FL_THIN_UP_BOX)
Fl:set_boxtype(fltk.FL_DOWN_BOX,fltk.FL_THIN_DOWN_BOX)

-- everything is local so it won't show up in the Global environment
local w,outpoot,title,backbutt,current_table
local my_depth=0
local my_level={}
local my_title={}
local my_line={}

-- text colors
local col_tab="@b"
local col_func="@C1"
local col_str="@C4"
local col_num="@C13"
local col_usr="@C8@B17"

local function lookittable()
  outpoot:clear()
  local my_value,my_string
  local tmptab={} -- temporary table for alphabetizing the list of my_string's
  for k,v in pairs(current_table) do
    my_value=tostring(k).."\t\t"..tostring(v)
    if     type(v)=="table" then my_string=col_tab..tostring(k)
    elseif type(v)=="function" then my_string=col_func..my_value
    elseif type(v)=="number" then my_string=col_num..my_value
    elseif type(v)=="userdata" then my_string=col_usr..my_value
    elseif type(v)=="string" then my_string=col_str..string.gsub(my_value,"\n"," %* ")
    else my_string=tostring(k)
    end
    table.insert(tmptab,my_string)
  end
  table.sort(tmptab) -- alphabetize
  for i,v in ipairs(tmptab) do outpoot:add(v) end -- add the sorted list to the browser
  title:label(my_title[my_depth])
  if my_depth==0 then backbutt:deactivate() else backbutt:activate() end
  outpoot:middleline(my_line[my_depth])
end

local function move_forward(k,v)
  my_depth=my_depth+1 -- move up a level
  my_level[my_depth]=current_table
  my_title[my_depth]=tostring(k).." ("..type(v)..")"
  my_line[my_depth]=1
  lookittable()
end

local function move_back()
if my_depth > 0 then
  current_table=my_level[my_depth-1]
  my_depth=my_depth-1
  lookittable()
end
end

local function which_is_it()
  local this_text=outpoot:text(outpoot:value())
  if outpoot:value() > 0 and type(current_table) == "table" then
    my_line[my_depth]=outpoot:value()
    -- Compare the browser text with each key in current_table until a match is found
    -- This could probably be streamlined
    for k,v in pairs(current_table) do
      -- it's a table
      if this_text == col_tab..tostring(k) then
        my_level[my_depth]=current_table --set a table to return to with "back"
        current_table=v
    move_forward(k,v)
        break
      end
      -- it's userdata
      if string.find(this_text,"^"..col_usr..tostring(k)) then
        my_level[my_depth]=current_table
        current_table=getmetatable(v)
    move_forward(k,v)
        break
      end
      -- it's a function
      if string.find(this_text,"^"..col_func..tostring(k))  then
        my_level[my_depth]=current_table
        -- this test fixes a crash, but doesn't supply the right info
        if string.find(this_text,"which_is_it") then getinfo_level=1 else getinfo_level=v end
        -- is there a way to get more useful info?
        current_table=debug.getinfo(getinfo_level,"nSlufL")
    move_forward(k,v)
        break
      end
    end
  end
end

local function mlexplore()
local ww=400
local wh=360

w=fltk:Fl_Double_Window(ww,wh,"view murga tables")

outpoot=fltk:Fl_Select_Browser(10,40,ww-20,wh-50)
if Fl_Browser.column_widths then outpoot:column_widths({(ww-20)/2,0}) end
outpoot:selection_color(0)
outpoot:callback(which_is_it)

title=fltk:Fl_Box(90,10,ww-100,30)
title:box(fltk.FL_THIN_UP_BOX)

backbutt=fltk:Fl_Button(10,10,80,30,"@<- back")
backbutt:callback(move_back)

w:resizable(outpoot)
current_table=_G
my_title[my_depth]="Global Environment (table: _G)"
my_line[my_depth]=1
lookittable()
w:show()
Fl:run()
end

mlexplore()

A little bit of code cleanup
Also, multiline strings now display on multiple lines in the browser. This is particularly visible with bind_lua_addtovtable()

Code:
-- everything is local so it won't show up in the Global environment
local w,outpoot,title,backbutt,current_table
local my_depth=0
local my_level={}
local my_title={}
local my_line={}

-- text colors
local col_tab="@b"
local col_func="@C1"
local col_str="@C4"
local col_num="@C13"
local col_usr="@C8@B17"

local function lookittable()
  outpoot:clear()
  local my_value,my_string
  local tmptab={} -- temporary table for alphabetizing the list of my_string's
  for k,v in pairs(current_table) do
    if type(v)=="string" then
      my_value=col_str..tostring(k).."\t"..string.gsub(tostring(v),"\n.*","")
      table.insert(tmptab,my_value)
      my_string=string.gsub(tostring(v),".-\n(.*)","%1")
      my_value=0
      for line in string.gmatch(my_string,".-\n") do
        my_value=1+my_value
        -- for multiline strings, pad numbers with zeros for proper sorting.
        table.insert(tmptab,col_str..tostring(k)..string.format('%03d',my_value).."\t"..line)
      end
    else
      my_value=tostring(k).."\t"..tostring(v)
      if     type(v)=="table" then my_string=col_tab..tostring(k)
      elseif type(v)=="function" then my_string=col_func..my_value
      elseif type(v)=="number" then my_string=col_num..my_value
      elseif type(v)=="userdata" then my_string=col_usr..my_value
      else my_string=tostring(k)
      end
      table.insert(tmptab,my_string)
    end --if
  end
  table.sort(tmptab) -- alphabetize
  for i,v in ipairs(tmptab) do outpoot:add(v) end -- add the sorted list to the browser
  title:label(my_title[my_depth])
  if my_depth==0 then backbutt:deactivate() else backbutt:activate() end
  outpoot:middleline(my_line[my_depth])
end

local function move_forward(k,v)
  my_depth=my_depth+1 -- move up a level
  my_level[my_depth]=current_table
  my_title[my_depth]=tostring(k).." ("..type(v)..")"
  my_line[my_depth]=1
  lookittable()
end

local function move_back()
if my_depth > 0 then
  current_table=my_level[my_depth-1]
  my_depth=my_depth-1
  lookittable()
end
end

local function which_is_it()
  local this_text=outpoot:text(outpoot:value())
  if outpoot:value() > 0 and type(current_table) == "table" then
    my_line[my_depth]=outpoot:value()
    -- Compare the browser text with each key in current_table until a match is found
    -- This could probably be streamlined
    for k,v in pairs(current_table) do
      -- it's a table
      if this_text == col_tab..tostring(k) then
        my_level[my_depth]=current_table --set a table to return to with "back"
        current_table=v
    move_forward(k,v)
        break
      end
      -- it's userdata
      if string.find(this_text,"^"..col_usr..tostring(k)) then
        my_level[my_depth]=current_table
        current_table=getmetatable(v)
    move_forward(k,v)
        break
      end
      -- it's a function
      if string.find(this_text,"^"..col_func..tostring(k))  then
        my_level[my_depth]=current_table
        -- is there a way to get more useful info?
        current_table=debug.getinfo(v,"nSlufL")
    move_forward(k,v)
        break
      end
    end
  end
end

local function mlexplore()
local ww=400
local wh=360

local win=fltk:Fl_Double_Window(ww,wh,"view murga tables")

outpoot=fltk:Fl_Select_Browser(10,40,ww-20,wh-50)
if Fl_Browser.column_widths then outpoot:column_widths({(ww-20)/2,0}) end
outpoot:selection_color(0)
outpoot:callback(which_is_it)

title=fltk:Fl_Box(90,10,ww-100,30)
title:box(fltk.FL_THIN_UP_BOX)

backbutt=fltk:Fl_Button(10,10,80,30,"@<- back")
backbutt:callback(move_back)

win:resizable(outpoot)
current_table=_G
my_title[my_depth]="Global Environment (table: _G)"
my_line[my_depth]=1
lookittable()
win:show()
Fl:run()
end

mlexplore()

[quote=mikshaw]
A little bit of code cleanup
Also, multiline strings now display on multiple lines in the browser. This is particularly visible with bind_lua_addtovtable()

Hi milkshaw

My first post!

Been hacking away at some fltk code and struggling to see the contents of tables my script was creating, ... so I hacked your EXCELLENT tablebrowser to be able to browse any table:

-- my changes:
-------------------
--changed:
--current_table=_G
--to:
current_table=tbl

--changed:
--mlexplore()
--to:
mlexplore(tbl)

--added at the end:
return mlexplore
----------------


From inside any script just do something like this:

tbrowse = require("tbrowse")
tbrowse(my_table)
... and voila! all is revealed

Perhaps someone else will find this useful, as I have!!! (wish I thought of it about 48 hours ago!!!)

Thanks for the great tool!!!
Reference URL's