WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   Beginner confused with table lua. (https://www.wowinterface.com/forums/showthread.php?t=32011)

eddiemars 04-21-10 02:34 PM

Beginner confused with table lua and layout-local frames.
 
Edit: Scroll down a couple comments if you wish to see my layout-local frame question, my frame question having been answered so speedily.

Hello. While I am sure this is a very simple problem I am having confusions on how to execute this. The table I am wanting is essentially a list of Minimap Children (though I want to remove some items), and I have included code in the lua to put this into my chat frame to make sure it is working. Okay easy:
Code:

function AMget(self)
        local kiddies = { Minimap:GetChildren() }
        for _, frame in pairs(kiddies) do
                DEFAULT_CHAT_FRAME:AddMessage( frame:GetName() );
        end
end

This works fine. Great, now I know everything that uses Minimap as a parent. I then see however, that a couple things use it as a parent that I do not want in my table. MinimapPing and MinimapBackdrop. So now I want to figure out a way to have my 'kiddies' table say everything it did before, minus those two things I mentioned. So I researched into 'tremove' and I have fiddled with that but I am very confused with two things: how would I assign tremove with a position when all I have is a name (a name which might change position depending on what is loaded in game), and how do I impliment tremove(table, position) into the code?

Any help would be much appreciated, I am finding the online resources to be far too vague for me to piece this together. :confused:

Xus 04-21-10 03:00 PM

Code:

function AMget(self)
        local kiddies, i = { Minimap:GetChildren() }, 1;
        DEFAULT_CHAT_FRAME:AddMessage( "before:" );
        for _, frame in ipairs(kiddies) do
                DEFAULT_CHAT_FRAME:AddMessage( frame:GetName() );
        end
       
        --remove MinimapPing and MinimapBackdrop
        while i <= #kiddies do
                if kiddies[i]:GetName() == "MinimapPing" or kiddies[i]:GetName() == "MinimapBackdrop" then
                        tremove(kiddies,i);
                else
                        i=i+1;
                end
        end
       
        -- do other stuff!
        DEFAULT_CHAT_FRAME:AddMessage( "after:" );
        for _, frame in ipairs(kiddies) do
                DEFAULT_CHAT_FRAME:AddMessage( frame:GetName() );
        end
end

this is not the uber-most-efficient way, but it works fine :)

eddiemars 04-21-10 03:30 PM

works perfect, thank you! Also this teaches me a good deal too. I definitely learn best when I see these things in use. :)

eddiemars 04-21-10 06:10 PM

Another question, since I don't feel like thread spamming I'll just tack it on here, and if no one sees it that's okay.

Okay so I've been playing around a lot with various frames doing:
Code:

frame:RegisterForDrag("LeftButton")
frame:SetClampedToScreen(true)
frame:SetMovable(true)
frame:SetScript("OnDragStart", function(self) if IsAltKeyDown() then self:StartMoving() end end)
frame:SetScript("OnDragStop", function(self) if IsAltKeyDown() then self:StopMovingOrSizing() end end)

I find it works for just about every frame indeed. BUT, when I /reload, it only saves the position of a couple. MinimapMailFrame for example, that gets saved perfectly. TimeManagerClockButton however, it can be moved yes, but goes back to it's original position each time after a /reload. I have also tried calling frame:ClearAllPoints() (also I tried frame:SetUserPlaced(true), this just makes errors) but it seems to make no difference at all. Does anyone know either why this happens to only some frames, or a way to stop this resetting from happening?

Dridzt 04-21-10 06:14 PM

Code:

local skipframes = {}
skipframes["MinimapPing"] = true
skipframes["MinimapBackdrop"] = true

local children = {}

function GetChildrenTree(Frame)
  if Frame:GetChildren() then
    for _,child in pairs({Frame:GetChildren()}) do
      if not skipframes[child:GetName()] then
        tinsert(children,child);
      end
      GetChildrenTree(child);
    end
  end
end

GetChildrenTree(Minimap)

This is a recursive function so it will call itself until it gets all the Minimap children (not just the first level ones).
It will also not add anything found in skipframes instead of adding them
just to remove them again later.

eddiemars 04-21-10 06:59 PM

Quote:

Originally Posted by Dridzt (Post 185472)
This is a recursive function so it will call itself until it gets all the Minimap children (not just the first level ones).
It will also not add anything found in skipframes instead of adding them
just to remove them again later.

Oh this is quite nice looking indeed, thank you.

Akryn 04-21-10 09:11 PM

Quote:

Originally Posted by eddiemars (Post 185471)
Does anyone know either why this happens to only some frames, or a way to stop this resetting from happening?

http://www.wowwiki.com/API_Frame_SetUserPlaced

(or alternatively, save the position(s) in a PLAYER_LEAVING_WORLD event handler and restore on VARIABLES_LOADED or similar.)

Edit: I missed that you were talking about Bliz frames. A lot of them try to make sure they're in the "correct" place, and you'd have to override that. There are a number of other threads about doing that.

Sideshow 04-22-10 03:57 AM

something i still did not read anywere:

what's the difference between pairs and ipairs ?
what is this "_" thing ?

would be great to know

also, in my addon, i use a standard for k,v in pairs(mytable) do
but i don't even use the v in the script .... I reckon it takes a "long" time for the cpu to find the v though ...

ravagernl 04-22-10 05:20 AM

Quote:

Originally Posted by Sideshow (Post 185519)
something i still did not read anywere:

what's the difference between pairs and ipairs ?

Pairs will iterate over all indexes in a table. Ipairs can only iterate over a table using integers as indexes. See http://www.lua.org/pil/7.3.html
Quote:

what is this "_" thing ?

would be great to know
Say a function returns multiple variables, and you only want the second variable returned. A perfect example would be UnitClass, the first variable is the localized name of the class, the second is the english name in capitals.:
Code:

function UnitClass(unit)
    return "Mage", "MAGE"
end

local localClass, englishClass = UnitClass('player')

Right, so we now have both versions of the class... But what if you only need the second variable? You could use select, but that will iterate over the variables returned, which is not necesary. So we use the underscore character. It's supposed to be a variable that nobody will use in it's code (who will ever name his variable _?). So we use:
Code:

local _, class = UnitClass('player')
if class == 'MAGE' then
    -- Do some stuff here for mages only.
end

Quote:

also, in my addon, i use a standard for k,v in pairs(mytable) do
but i don't even use the v in the script .... I reckon it takes a "long" time for the cpu to find the v though ...
Advice about that depends on the table structure and what you want to do with the information inside of it. List your code if you are unsure if you are doing it the most efficient way :)

Sideshow 04-22-10 05:39 AM

Code:

for k,v in pairs(com) do
        if not com[k][5] and not com[k][6] and com[k][1] > 0 then -- if it's not a pet and there is not yet a bar and damage > 0 then make a bar
                makeBar(k)
        end
        if com[k][6] then -- if there is a bar, update value and text (the text fontstring is referenced in [7])
                com[k][6]:SetValue(com[k][1]/mostDamage*1000)
                com[k][7]:SetText(com[k][1]..' ('..ceil(com[k][1]/com[k][2])..')')
        end
end

it's an iteration to update the statusbar object for each instance of "com" (combatants for a dps meter)
as you see, "v" is never used at all, since i use integer indexes
the structure of an instance in com is made like : com[UnitGUID] = { 0, 3.5, nil, "", "", nil, nil }

Dridzt 04-22-10 06:55 AM

'for k,v in pairs(mytable) do'
expand the variable names and it comes closer to natural language:
'for key, value in pairs(mytable) do
-- stuff with key
-- or stuff with value
end

local vehicles = {"bicycle", "car", "truck"}
this table has implicit integer keys it is the equivalent of
local vehicles = {
[1] = "bicycle",
[2] = "car",
[3] = "truck",
}

ipairs works on a subset of lua tables that has sequentially indexed values like above.
for index, value in ipairs(vehicles) do
print("vehicle:"..value)
end

The table above is one where the information (the vehicle) is held by the value.

ipairs will not work on a table with non-sequential integer keys or other type of keys.
for example:
local polluters = {
["bicycle"] = false,
["car"] = true,
["truck"] = true,
}
pairs has to be used here
for key, value in pairs(polluters) do
if polluters[key] then
print("gas masks on")
end
end

Torhal 04-22-10 10:13 AM

Quote:

Originally Posted by Dridzt (Post 185532)
local polluters = {
["bicycle"] = false,
["car"] = true,
["truck"] = true,
}

Not only would leaving "bicycle" out of the list not alter how it works, but it doesn't allocate the byte required for the boolean. This could be considered nitpicking, but using this technique allowed me to decrease memory usage for ARL by roughly 40%.

eddiemars 04-22-10 10:29 AM

Quote:

Originally Posted by Akryn (Post 185500)
(or alternatively, save the position(s) in a PLAYER_LEAVING_WORLD event handler and restore on VARIABLES_LOADED or similar.)

Edit: I missed that you were talking about Bliz frames. A lot of them try to make sure they're in the "correct" place, and you'd have to override that. There are a number of other threads about doing that.

Spent a while searching for threads about it but I came up nil. You think if I just set up a saved variables it will override the correcting of blizz's frames?

Dridzt 04-22-10 11:54 AM

Quote:

Originally Posted by Torhal (Post 185546)
Not only would leaving "bicycle" out of the list not alter how it works, but it doesn't allocate the byte required for the boolean. This could be considered nitpicking, but using this technique allowed me to decrease memory usage for ARL by roughly 40%.

You're absolutely right I was aiming for ease of understanding, obviously nil is equivalent and better than setting false in the example I provided.

nightcracker 04-22-10 11:56 AM

Quote:

Originally Posted by Dridzt (Post 185560)
You're absolutely right I was aiming for ease of understanding, obviously nil is equivalent and better than setting false in the example I provided.

Note that nil activates the metatable __index while false doesn't.

Xrystal 04-22-10 12:27 PM

Quote:

Originally Posted by eddiemars (Post 185549)
Spent a while searching for threads about it but I came up nil. You think if I just set up a saved variables it will override the correcting of blizz's frames?

Yes, if you store the position you want them at before logging off. Then when you log back in, under the VARIABLES_LOADED event reanchor accordingly. This event triggers after the automatic layout file is utilised which affects sizing and positioning.

Just utilised this myself when I lost my frame whilst trying to figure out the in game frame sizing feature. The frame sizing was given up for the moment as I have a quite fine UI to set the sizes but by just switching my positioning and sizing code to be called after the blizz vars are loaded helped bring my frame back.

This is the page I utilised my learning process about loading order from.. http://www.wowwiki.com/Events_that_f...oading_Process

eddiemars 04-22-10 03:09 PM

Quote:

Originally Posted by eddiemars (Post 185549)
You think if I just set up a saved variables

Wow, okay. Saved Variables making is so confusing! I've followed a couple guides (ie: http://www.wowwiki.com/Saving_variab..._game_sessions) but, none of them explain how to put frame placement info into the savedvariable file. I'm sure it's supposed to be self explanatory but I am very lost. Anyone know a better (or more frame specific) tutorial for this? Or maybe you enjoy helping out nubs, for those of you that do I will include the very bungled .lua I have going on. As it is right now, it just does nothing. If you think the file is a total lost cause by all means don't feel compelled to help out just because I'm asking! I understand! I do however truly appreciate all the help.

Code:

local frame = CreateFrame("Frame", "AsteroidMinimapButtons", UIParent)
frame:RegisterEvent("ADDON_LOADED")
frame:RegisterEvent("PLAYER_LOGOUT")

function frame:OnEvent(event, arg1)
        if event == "ADDON_LOADED" and arg1 == "AsteroidMinimapButtons" then

                local kiddies, i = { Minimap:GetChildren() }, 1;
                --remove
                while i <= #kiddies do
                        if kiddies[i]:GetName() == "MinimapPing" or kiddies[i]:GetName() == "MinimapBackdrop" or kiddies[i]:GetName() == "AsteroidMinimap" or kiddies[i]:GetName() == "AsteroidFrame" then
                                tremove(kiddies,i);
                        else
                                i=i+1;
                        end
                end

                if not AstroDB or AstroDB == nil then
                        for _, framex in ipairs(kiddies) do
                        DEFAULT_CHAT_FRAME:AddMessage( framex:GetName() )
                                framex:RegisterForDrag("LeftButton")
                                framex:SetClampedToScreen(true)
                                framex:SetMovable(true)
                                framex:SetScript("OnDragStart", function(self) if IsAltKeyDown() then self:StartMoving() end end)
                                framex:SetScript("OnDragStop", function(self) if IsAltKeyDown() then self:StopMovingOrSizing() end end)
                        end
                AstroDB = --??So confused.
                end

                if AstroDB then
                        for _, framex in ipairs(kiddies) do
                        DEFAULT_CHAT_FRAME:AddMessage( framex:GetName() )
                                point, relativeTo, relativePoint, xOfs, yOfs = framex:GetPoint()
                                framex:SetPoint(point, relativeTo, relativePoint, xOfs, yOfs)
                                framex:RegisterForDrag("LeftButton")
                                framex:SetClampedToScreen(true)
                                framex:SetMovable(true)
                                framex:SetScript("OnDragStart", function(self) if IsAltKeyDown() then self:StartMoving() end end)
                                framex:SetScript("OnDragStop", function(self) if IsAltKeyDown() then self:StopMovingOrSizing() end end)
                        end
                end

        elseif event == "PLAYER_LOGOUT" then
                local kiddies, i = { Minimap:GetChildren() }, 1;
                --remove
                while i <= #kiddies do
                        if kiddies[i]:GetName() == "MinimapPing" or kiddies[i]:GetName() == "MinimapBackdrop" or kiddies[i]:GetName() == "AsteroidMinimap" or kiddies[i]:GetName() == "AsteroidFrame" then
                                tremove(kiddies,i);
                        else
                                i=i+1;
                        end
                end

                for _, framex in ipairs(kiddies) do
                        point, relativeTo, relativePoint, xOfs, yOfs = framex:GetPoint()
                end
        AstroDB = { framex:GetPoint() }--???
        end
end

I tried putting in savedvariable info to my original file, but it never worked that way either, so this version I have linked is the wowwiki tutorial version copy pasted with my own addon's info and desired functions.

Here's the working version without a savedvariables. (Well, sort of working, like I said before the reason I want saved variables is to keep the positions of certain frames that Blizz is overriding like TimeManagerClockButton. Also it loads the function a million times for each Addon, I triesd to add a 'if arg1' deal but I think my SetScript is messy, anyways.)

Code:

local AMwait = CreateFrame("Frame","AMWaitFrame", UIParent)

local event = function (self)
        local kiddies, i = { Minimap:GetChildren() }, 1;
        --remove
        while i <= #kiddies do
                if kiddies[i]:GetName() == "MinimapPing" or kiddies[i]:GetName() == "MinimapBackdrop" or kiddies[i]:GetName() == "AsteroidMinimap" or kiddies[i]:GetName() == "AsteroidFrame" then
                        tremove(kiddies,i);
                else
                        i=i+1;
                end
        end

        for _, framex in ipairs(kiddies) do
        DEFAULT_CHAT_FRAME:AddMessage( framex:GetName() )
                framex:RegisterForDrag("LeftButton")
                framex:SetClampedToScreen(true)
                framex:SetMovable(true)
                framex:SetScript("OnDragStart", function(self) if IsAltKeyDown() then self:StartMoving() end end)
                framex:SetScript("OnDragStop", function(self) if IsAltKeyDown() then self:StopMovingOrSizing() end end)
        end
end

AMwait:SetScript("OnEvent", event)
AMwait:RegisterEvent"ADDON_LOADED"


SDPhantom 04-22-10 04:31 PM

Quote:

Originally Posted by Sideshow (Post 185519)
something i still did not read anywere:

what's the difference between pairs and ipairs ?
what is this "_" thing ?

would be great to know

also, in my addon, i use a standard for k,v in pairs(mytable) do
but i don't even use the v in the script .... I reckon it takes a "long" time for the cpu to find the v though ...

1) pairs() iterates through all entries of a table, don't expect any sort of order in which entries are found first. ipairs() iterates only through entries with numeric indices starting at 1 and continuing until it runs into a nil entry.

2) "_" is a legal variable name in Lua, most commonly used just to dump any returned values into that aren't needed.

3) You may omit "v" and it should only remember the keys. The function will still return a second value for what's contained in the iterated entry, but Lua will then toss it away since there's no variable to assign it to. The function will always run the same no matter if you assign all return values to a variable or not.

A note on the performance of for k,v in pairs(t), all core Lua functions as well as the WoW API are written purely in C. This is extremely fast compared to writing the same function and having it run through the interpreter. As far as iterating through tables, it takes the same amount of time to find the keys as it is to find the associated values.

Xrystal 04-22-10 05:11 PM

Quote:

Originally Posted by eddiemars (Post 185579)
Wow, okay. Saved Variables making is so confusing! I've followed a couple guides (ie: http://www.wowwiki.com/Saving_variab..._game_sessions) but, none of them explain how to put frame placement info into the savedvariable file. I'm sure it's supposed to be self explanatory but I am very lost. Anyone know a better (or more frame specific) tutorial for this? Or maybe you enjoy helping out nubs, for those of you that do I will include the very bungled .lua I have going on. As it is right now, it just does nothing. If you think the file is a total lost cause by all means don't feel compelled to help out just because I'm asking! I understand! I do however truly appreciate all the help.
*snipped*

This is how I've recently cottoned onto dealing with it.

I have in my SavedVariables table these set of values:
Code:

        ["ptSelf"] = "CENTER",
        ["anchor"] = "UIParent",
        ["ptAnchor"] = "CENTER",
        ["xOffset"] = 0,
        ["yOffset"] = 0,

I have the anchor in text to store the name of the frame that is being used as an anchor. It's easier to do _G[name] to get the frame than to have the frame not store in the saved variables ... *table omitted etc comment in WTF files rofl*

This is how I initially fill the table up after the frame has been created. I have a set of hard coded defaults set up that the addon can fall back onto if problems arise or the user after doing some customising gets messed up and whats to reset everything rofl.
Code:

xsf.InitialiseVariables = function(self)
        XSF_Data = XSF_Data or {};
        XSF_Data[self:GetName()] = XSF_Data[self:GetName()] or {};
        self.Data = XSF_Data[self:GetName()];
                               
        self.Data["ptSelf"]                = self.Data["ptSelf"]                        or self.Defaults["ptSelf"];
        self.Data["anchor"]                = self.Data["anchor"]                        or self.Defaults["anchor"];
        self.Data["ptAnchor"]                = self.Data["ptAnchor"]                or self.Defaults["ptAnchor"];
        self.Data["xOffset"]                = self.Data["xOffset"]                        or self.Defaults["xOffset"];
        self.Data["yOffset"]                = self.Data["yOffset"]                        or self.Defaults["yOffset"];
end

I then have these scripts set up to carry out the movement and when it is finished store the final anchor position. xsf being the name of the frame in question.

Code:

        xsf.OnMouseDown = function(self)
                if ( not self ) then return; end
                if ( ( not self.isLocked ) or ( self.isLocked == 0 ) ) then
                        if ( self:IsMovable() ) then
                                self:StartMoving();
                                self.isMoving = true;
                                self.hasMoved = false;
                        end
                end
        end

        xsf.OnMouseUp = function(self)
                if ( not self ) then return; end
                if ( self.isMoving ) then
                        self:StopMovingOrSizing();
                        if ( self.isMoving ) then
                                self.isMoving = false;
                                self.hasMoved = true;
                        end
                end
                if ( self.hasMoved and self.StoreAnchor ) then
                        self:StoreAnchor();
                end
        end       

        xsf.OnHide = function(self)
                if ( not self ) then return; end
                if ( self.isMoving ) then
                        self:StopMovingOrSizing();
                        self.isMoving = false;
                end
        end

I then have 3 anchor functions. StoreAnchor, GetAnchor and SetAnchor as follows. self.Data is a table link to the saved variables table segment for this frame:
Code:

xsf.SetAnchor = function(self)
        if ( self.isLocked ) then return end
        local ptSelf = self.Data["ptSelf"];
        local anchor = self.Data["anchor"];
        local ptAnchor = self.Data["ptAnchor"];
        local xOffset = self.Data["xOffset"];
        local yOffset = self.Data["yOffset"];
        self:ClearAllPoints();
        self:SetPoint(ptSelf,_G[anchor],ptAnchor,xOffset,yOffset);
end

xsf.GetAnchor = function(self)
        ptSelf,anchor,ptAnchor,xOffset,yOffset = self:GetPoint(1);       
        if ( anchor ) then
                anchor = anchor:GetName();
        else
                anchor = self:GetParent():GetName();
        end
        return ptSelf,anchor,ptAnchor,xOffset,yOffset;
end
       
xsf.StoreAnchor = function(self)       
        local ptSelf,anchor,ptAnchor,xOffset,yOffset = self:GetAnchor();
        if ( not ptSelf ) then
                ptSelf = self.Data["ptSelf"];
                anchor = self.Data["anchor"];
                ptAnchor = self.Data["ptAnchor"];
                xOffset = self.Data["xOffset"];
                yOffset = self.Data["yOffset"];
        end
        self.Data["ptSelf"] = ptSelf;
        self.Data["anchor"] = anchor;
        self.Data["ptAnchor"] = ptAnchor;
        self.Data["xOffset"] = xOffset;
        self.Data["yOffset"] = yOffset;
end

And here is the resulting WTF saved variables output since I last logged out after playing with this code. Notice it keeps track of the 'nearest anchor point' that blizz automatically uses to anchor too.:
Code:

XSF_Data = {
        ["XSFrame"] = {
                ["ptAnchor"] = "CENTER",
                ["yOffset"] = -11.31710682698755,
                ["xOffset"] = 0.9366468145787907,
                ["anchor"] = "TestFrame",
                ["ptSelf"] = "CENTER",
        },
}

So basically I do the following:

1. Initialise anchor as center of UIParent.
2. Make any necessary changes to anchor if the parent is no longer UIParent.
3. After moving the frame store the anchor settings.
4. After blizzard has finished its own frame movement code ( VARIABLES_LOADED) then SetAnchor to what we last stored it as.

I have some other functions set up as well to handle changes on an addon by addon basis as this is all in a file that I plan to make generic to all my addons needing its functionality but this is enough to handle a one on one frame setup.

So far it has been working as I expected. You could even validate what GetPoint returns and change it if you want it to store it a different way. Also, you could always add extra points and utilise GetPoint and the other functions to handle all points the frames have and save them all. In my case the anchors being saved are generally single anchors so I haven't needed to expand onto that area yet :D

eddiemars 04-22-10 06:14 PM

Quote:

Originally Posted by Xrystal (Post 185586)
This is how I've recently cottoned onto dealing with it.

Okay this will be helpful indeed. Thank you! So many addons use savedvariables for frames you'd think I could find an example but everything I looked at also seemed to use libs which just made me more confused. :)


All times are GMT -6. The time now is 10:05 AM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI