Thread Tools Display Modes
07-09-12, 12:45 AM   #1
Brusalk
An Aku'mai Servant
 
Brusalk's Avatar
AddOn Author - Click to view addons
Join Date: May 2010
Posts: 32
Determining a frame from a normal table?

Hey There!

So in my addon I need to find a way to differentiate between a table value and a frame value (or texture, fontString, etc).

I've been trying to figure it out, and I can't seem to figure out a way to differentiate it without throwing an error that the method doesn't exist. Type(tableorframename) returns table regardless.


To be clear: I'm trying to do something like

if IsFrame(temp) then -- do stuff since it's a frame
elseif type(temp) == "table" then -- do other stuff since it's a table and not a frame
else -- idk
end

And no, I'm not trying to make a frame if it doesn't exist, I'm trying to differentiate between a table and a frame/widget.


Thanks!
-Brusalk
  Reply With Quote
07-09-12, 01:52 AM   #2
Barjack
A Black Drake
AddOn Author - Click to view addons
Join Date: Apr 2009
Posts: 89
There may be a more sophisticated way to do this, but you don't need to call a function to find out if it exists or not, so you can use that to kind of check whether something is (probably) a widget. For example:

Lua Code:
  1. if (obj.GetObjectType) then
  2.   -- it's a widget of some sort
  3.   if (obj:GetObjectType() == "Frame") then
  4.     -- do whatever. could also be "Button", "StatusBar", etc.
  5.   end
  6. else
  7.   -- it's a table
  8. end

Of course if someone actually defines a GetObjectType on their table which isn't really a frame, this might backfire. I don't know if that's actually relevant for your purposes though.
  Reply With Quote
07-09-12, 02:01 AM   #3
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by Brusalk View Post
So in my addon I need to find a way to differentiate between a table value and a frame value (or texture, fontString, etc).
For most purposes:

Code:
if type(object) == "table" then
     if object.GetObjectType then
          -- it's a frame, texture, font string, or other UI widget
     else
          -- it's a table
     end
end
Slightly slower but slightly more foolproof if you're worried about tables "pretending" to be widgets by defining GetObjectType or methods on themselves:

Code:
if type(object) == "table" then
    if type(object[0]) == "userdata" then
        -- it's a widget
    else
        -- it's a table
    end
end
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
07-09-12, 03:28 PM   #4
Brusalk
An Aku'mai Servant
 
Brusalk's Avatar
AddOn Author - Click to view addons
Join Date: May 2010
Posts: 32
Awesome, thanks!

I tried doing a /run print(type(tableVar[0])) and it was returning table when I tried to do that last night. (I defined tableVar as a global frame by /run tableVar = CreateFrame("frame")


On another note, is there a way to make your own UI widget that new frames can inherit off of? I'm working in my addon with a frame which contains a bunch of methods and variables which I'm interested in duplicating (exactly the same as creating a frame which inherits something like a PaperDollFrame or something like that)
  Reply With Quote
07-09-12, 04:41 PM   #5
Dridzt
A Pyroguard Emberseer
 
Dridzt's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2005
Posts: 1,360
Originally Posted by Brusalk View Post
Awesome, thanks!

I tried doing a /run print(type(tableVar[0])) and it was returning table when I tried to do that last night. (I defined tableVar as a global frame by /run tableVar = CreateFrame("frame")


On another note, is there a way to make your own UI widget that new frames can inherit off of? I'm working in my addon with a frame which contains a bunch of methods and variables which I'm interested in duplicating (exactly the same as creating a frame which inherits something like a PaperDollFrame or something like that)
There's 2 ways (that I know) to do this.
a. Use xml for your template frame and set the virtual attribute to true Wowpedia: Using templates.
b. Use a frame constructor function that returns a frame if you want to achieve a similar effect in pure lua.
  Reply With Quote
07-09-12, 05:48 PM   #6
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Code:
local function NewItemButton()
	local button = CreateFrame("Button", nil, UIParent)
	button.text = button:CreateFontString(nil, "OVERLAY")
	button.icon = button:CreateTexture(nil, "OVERLAY")
	-- Set up scripts, regions, and other features
	-- that are shared by all buttons here.
end

local button1 = NewItemButton()
button1:SetPoint("CENTER", 0, 200)
-- Set points, icon textures, fontstring text, and other features
-- that are specific to this individual button here.

local button2 = NewItemButton()
button2:SetPoint("RIGHT", -100, 0)
Or, you can use a metatable:

Code:
local buttons = setmetatable({}, { __index = function(t, i)
	local button = CreateFrame("Button", nil, UIParent)
	button.text = button:CreateFontString(nil, "OVERLAY")
	button.icon = button:CreateTexture(nil, "OVERLAY")

	-- Set up scripts, regions, and other features
	-- that are shared by all buttons here.

	if i > 1 then
		-- Not the first button; attach to previous button
		button:SetPoint("TOP", buttons[i-1], "BOTTOM")
	else
		-- First button; no previous button to attach to
		button:SetPoint("TOP", UIParent, "CENTER", 0, 200)
	end

	t[i] = button
	return button
end })

for i = 1, 10 do
	-- This will create 10 buttons, even though you never explicitly call any functions to create them.
	local button = buttons[i]
	button.text:SetText("Button #"..i)
	button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
end
Depending on what kind of objects you want to create, and how you're using them, one method will make more sense than the other. Both make more sense than XML though.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.

Last edited by Phanx : 07-09-12 at 10:09 PM.
  Reply With Quote
07-09-12, 06:14 PM   #7
Brusalk
An Aku'mai Servant
 
Brusalk's Avatar
AddOn Author - Click to view addons
Join Date: May 2010
Posts: 32
Is there a specific speed increase one way over the other and how does WoW/Lua handle the creation of frames using a metatable? Like, they do exactly the same thing, but does one require less memory or execution time or is one faster in completing execution?

When you're defining the metatable, there's __index = function(t, i). Is t a reference to the table that's created or is it a reference to the base object. (From brief reading of metatables in the Lua-man, I see that __index is a metamethod that is basically a catch-all) For example, say I wanted to make a series of new frames that all share some common properties.

I'd define a metatable (using setmetatable) giving it all the stuff you included in your example, letting me make a bunch of frames easily by indexing the metatable. Now, say I wanted to define methods on the created frames, such as returning a custom value contained in the frame. So I would add something like this?

Code:
local buttons = setmetatable({}, { __index = function(t, i)
	local button = CreateFrame("Button", nil, UIParent)
	button.text = button:CreateFontString(nil, "OVERLAY")
	button.icon = button:CreateTexture(nil, "OVERLAY")

	-- Set up scripts, regions, and other features
	-- that are shared by all buttons here.

        function button:GetIconTexture()
              return self.icon:GetTexture()
        end

	if i > 1 then
		-- Not the first button; attach to previous button
		button:SetPoint("TOP", buttons[i-1], "BOTTOM")
	else
		-- First button; no previous button to attach to
		button:SetPoint("TOP", UIParent, "CENTER", 0, 200)
	end
end })

for i = 1, 10 do
	-- This will create 10 buttons, even though you never explicitly call any functions to create them.
	local button = buttons[i]
	button.text:SetText("Button #"..i)
	button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
        print(button:GetIconTexture()) -- prints "Interface\\Icons\\INV_MISC_QuestionMark"
end
So adding that would allow me to hide some commonly used implementation to clean up other sections of code, or am I wrong and misunderstanding something?
  Reply With Quote
07-09-12, 09:33 PM   #8
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,327
Originally Posted by Phanx View Post
Code:
local buttons = setmetatable({}, { __index = function(t, i)
	local button = CreateFrame("Button", nil, UIParent)
	button.text = button:CreateFontString(nil, "OVERLAY")
	button.icon = button:CreateTexture(nil, "OVERLAY")

	-- Set up scripts, regions, and other features
	-- that are shared by all buttons here.

	if i > 1 then
		-- Not the first button; attach to previous button
		button:SetPoint("TOP", buttons[i-1], "BOTTOM")
	else
		-- First button; no previous button to attach to
		button:SetPoint("TOP", UIParent, "CENTER", 0, 200)
	end
end })

for i = 1, 10 do
	-- This will create 10 buttons, even though you never explicitly call any functions to create them.
	local button = buttons[i]
	button.text:SetText("Button #"..i)
	button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
end
You need the __index metamethod to return a value or buttons[i] will still retrieve nil. Also to prevent the table from recreating extra buttons constantly, you should actually set the value in the table.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
07-09-12, 09:55 PM   #9
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,327
Originally Posted by Brusalk View Post
Is there a specific speed increase one way over the other and how does WoW/Lua handle the creation of frames using a metatable? Like, they do exactly the same thing, but does one require less memory or execution time or is one faster in completing execution?
If you really want to pick at it, the metatable is an extra table lookup and value check on top of the function call. That being said, it's still a minimal difference between the two.



Originally Posted by Brusalk View Post
When you're defining the metatable, there's __index = function(t, i). Is t a reference to the table that's created or is it a reference to the base object. (From brief reading of metatables in the Lua-man, I see that __index is a metamethod that is basically a catch-all) For example, say I wanted to make a series of new frames that all share some common properties.
t is a reference to the base object you applied the metatable to. In the case of Phanx's example code, this would be the table buttons.



Originally Posted by Brusalk View Post
I'd define a metatable (using setmetatable) giving it all the stuff you included in your example, letting me make a bunch of frames easily by indexing the metatable. Now, say I wanted to define methods on the created frames, such as returning a custom value contained in the frame. So I would add something like this?

...

So adding that would allow me to hide some commonly used implementation to clean up other sections of code, or am I wrong and misunderstanding something?
That would work, but if you're going to define a static function for use multiple times, you should declare it as an upvalue then set a reference to it.

Note: This example incorporates the concerns I added about Phanx's code above.
Lua Code:
  1. local function Button_GetIconTexture(self)--    Note obj:GetTexture() is the same as obj.GetTexture(obj)
  2.     return self.icon:GetTexture();
  3. end
  4.  
  5. local buttons=setmetatable({},{__index=function(t,i)
  6.     local button = CreateFrame("Button", nil, UIParent);
  7.     button.text = button:CreateFontString(nil, "OVERLAY");
  8.     button.icon = button:CreateTexture(nil, "OVERLAY");
  9.  
  10. --  Set up scripts, regions, and other features
  11. --  that are shared by all buttons here.
  12.  
  13.     button.GetIconTexture=Button_GetIconTexture;
  14.  
  15.     if i > 1 then
  16. --      Not the first button; attach to previous button
  17.         button:SetPoint("TOP", buttons[i-1], "BOTTOM");
  18.     else
  19. --      First button; no previous button to attach to
  20.         button:SetPoint("TOP",UIParent,"CENTER",0,200);
  21.     end
  22.  
  23.     t[i]=button;
  24.     return button;
  25. end});
  26.  
  27. for i = 1, 10 do
  28. --  This will create 10 buttons, even though you never explicitly call any functions to create them.
  29.     local button=buttons[i];
  30.     button.text:SetText("Button #"..i);
  31.     button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark");
  32.     print(button:GetIconTexture());--   prints "Interface\\Icons\\INV_MISC_QuestionMark"
  33. end
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
07-09-12, 10:14 PM   #10
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by SDPhantom View Post
You need the __index metamethod to return a value or buttons[i] will still retrieve nil. Also to prevent the table from recreating extra buttons constantly, you should actually set the value in the table.
Fixed; that's what I get for posting while I'm eating lunch.

Originally Posted by SDPhantom View Post
If you really want to pick at it, the metatable is an extra table lookup and value check on top of the function call.
If you weren't going to store the buttons (or whatever widgets) in a table, then yes, the metatable is slightly more overhead due to the tables. But most likely, if you need to generate a lot of identical objects, you're going to be (or should be) storing them in a table anyway, so you're only adding overhead the first time you access each object and create it. Personally, I find it more convenient to not have to worry about explicitly checking if the object exists and explicitly creating it, but it really just comes down to what you prefer.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
07-09-12, 11:26 PM   #11
Brusalk
An Aku'mai Servant
 
Brusalk's Avatar
AddOn Author - Click to view addons
Join Date: May 2010
Posts: 32
Interesting. So SDPhantom, could you explain a little bit more about upvalues? I'm reading through the documentation on up-values, but from what I can see it's just a term for a variable that's local to the specific object's instance, or am I misinterpreting what they're saying?

And in the code you provided:
Code:
button.GetIconTexture=Button_GetIconTexture;
Yet you access self in the local function Button_GetIconTexture(). My understanding was that you could only access the self var if the function was defined as a method of a table/object.
  Reply With Quote
07-10-12, 02:09 AM   #12
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,327
Originally Posted by Brusalk View Post
Interesting. So SDPhantom, could you explain a little bit more about upvalues? I'm reading through the documentation on up-values, but from what I can see it's just a term for a variable that's local to the specific object's instance, or am I misinterpreting what they're saying?
Upvalues are locals defined in a higher scope. In this example, VarA is an upvalue to Func() while VarB is merely a local that only exists within it.

Code:
local VarA="Some value";

function Func()
	local VarB="Some other value";
--	Do something here
end




Originally Posted by Brusalk View Post
And in the code you provided:
Code:
button.GetIconTexture=Button_GetIconTexture;
Yet you access self in the local function Button_GetIconTexture(). My understanding was that you could only access the self var if the function was defined as a method of a table/object.
Any function called as object:function() will have object passed as its first arg. Any other arguments are passed after the first. If a function is defined in that same syntax, the first arg (the reference to object) is always stored as the local variable self.

This means the 2 functions below are exactly the same in every way, even the existance of the self local, the value it contains, and the contents of the vararg expression.
Code:
function obj:func(...)
--	Do something with self here
end
Code:
function obj.func(self,...)
--	Do something with self here
end
Note on the second method, self can be renamed to something else, its value remains the same.



The second method can be extended to do something like this.
Code:
function func(self,...)
--	Do something with self here
end

--	Finally assign the function to the table
obj.func=func
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 07-10-12 at 02:21 AM.
  Reply With Quote

WoWInterface » Developer Discussions » General Authoring Discussion » Determining a frame from a normal table?


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off