for loop download image

Hello again gurus! KingN00b in the house!

Im trying to download some images from my server but am having trouble passing some json values into a function. I thought i had a pretty good idea of how to use for loops but i know im goofing up somewhere. This is what i have so far, but when i run this, the simulator just hangs on the activity monitor. the function runs perfectly when i hard code the valuse where v is.

  
function showImage()  
 native.setActivityIndicator( false )  
 testImage = display.newImage(v ,system.DocumentsDirectory,60,100);  
end  
  
function loadImage()  
 -- Create local file for saving data  
 local path = system.pathForFile(v, system.DocumentsDirectory )  
 local myFile = io.open( path, "w+b" )   
  
 native.setActivityIndicator( true ) -- show busy  
   
 -- Request remote file and save data to local file  
 http.request{   
 url = "http://thisiskel.com/logos/"..v,   
 sink = ltn12.sink.file(myFile),  
 }  
 timer.performWithDelay( 400, showImage)  
end  
for k,v in pairs(myLogoArray) do  
 print(v)  
 --[[  
 prints:  
 zippys.png  
 hilohatties.png  
 happyhaleiwa.png  
 nakamura.png  
 shirokiya.png  
 --]]  
 loadImage(v)  
 end  

Am i handling “v” incorrectly? Do i need to concatenate quotes around v in my loadImage function?

Thank you so much in advance. Im just starting out here and Im having a blast learning lua [import]uid: 88004 topic_id: 27139 reply_id: 327139[/import]

Well the first thing is that you call loadImage() with a parameter of v, but your function loadImage never takes a parameter.

v is likely local to the for loop and it’s not in scope inside of loadImage() where you are using it.

[import]uid: 19626 topic_id: 27139 reply_id: 110180[/import]

mind blown. thanks robmiracle! Okay made a couple tweaks and just moved the code around and it works!

However, how the code looks now doesn’t seem efficient. Do you have any tips for writing this in a cleaner way?

  
 for k,v in pairs(myLogoArray) do  
  
 function showImage()  
 native.setActivityIndicator( false )  
 testImage = display.newImage(v,system.DocumentsDirectory,60,100);  
 end  
  
 function loadImage()  
 -- Create local file for saving data  
 local path = system.pathForFile(v, system.DocumentsDirectory )  
 local myFile = io.open( path, "w+b" )   
  
 native.setActivityIndicator( true )   
 http.request{   
 url = "http://thisiskel.com/logos/"..v,   
 sink = ltn12.sink.file(myFile),  
 }  
 timer.performWithDelay( 400, showImage)  
 end  
 print(v)  
 loadImage()  
 end  

I really appreciate your input. Thanks!
-K [import]uid: 88004 topic_id: 27139 reply_id: 110185[/import]

Here is what I would have done:

function showImage(v)  
 native.setActivityIndicator( false )  
 testImage = display.newImage(v ,system.DocumentsDirectory,60,100);  
end  
   
function loadImage(v)  
 -- Create local file for saving data  
 local path = system.pathForFile(v, system.DocumentsDirectory )  
 local myFile = io.open( path, "w+b" )   
  
 native.setActivityIndicator( true ) -- show busy  
   
 -- Request remote file and save data to local file  
 http.request{   
 url = "http://thisiskel.com/logos/"..v,   
 sink = ltn12.sink.file(myFile),  
 }  
 timer.performWithDelay( 400, function() showImage(v) end)  
end  
   
   
for k,v in pairs(myLogoArray) do  
 print(v)  
 loadImage(v)  
end  

[import]uid: 19626 topic_id: 27139 reply_id: 110187[/import]

genius. thanks for taking a minute to help me out robmiracle [import]uid: 88004 topic_id: 27139 reply_id: 110201[/import]

Regarding for loops. Is it possible to have the loop skip the first number? something like:

for k, v in pairs(myTable) do  
 if k ==1 then  
 print("skip to number 2 and continue doing stuff")  
 end  
  
 table.insert(myBadges,myTable[k])  
 print(myBadges[k].badge)  
  
 badge = display.newImage("images/"..myBadges[k].badge,0,120)   
 badge:translate(100,(k-1) \* 102)  
 badge:scale(.75,.75)  
 badge:setReferencePoint(display.CenterReferencePoint);  
 badge:addEventListener("tap", ImageClicked)  
  
 local square = display.newRect(3,132,313,100)   
 square:translate(0,(k-1) \* 102)  
 coupGroup:insert(square)   
  
 local arrow = display.newImage("images/g\_coup\_arrow.png",0,140);  
 arrow:scale(.75,.75)  
 arrow:translate(250,(k-1) \* 102)  
 coupGroup:insert(arrow)   
 coupGroup:insert(badge)  
end  

[import]uid: 88004 topic_id: 27139 reply_id: 111595[/import]

any ideas? I need to put the first value into a different table [import]uid: 88004 topic_id: 27139 reply_id: 112088[/import]

There are two versions of a for loop.

One is using key, value pairs which is what you are doing. The other is to use an index counter:

for i = 1, 10 do  
 someTable[i] = somevalue  
end  

In this mode, you could go:

for i = 2, 10 do  
....  
end  

which would skip the first entry.

Since i don’t know how myLogoArray is created, you could since you’re not using the key anywhere do something like:

for i = 2, #myLogoArray do  
 print(i)  
 loadImage(i)  
end  

Putting a pound sign in front of an array name returns the number of elements in the array.
[import]uid: 19626 topic_id: 27139 reply_id: 112104[/import]

thank you so much for your help robmiracle! [import]uid: 88004 topic_id: 27139 reply_id: 112113[/import]

*secretly lurks*

:slight_smile: [import]uid: 61600 topic_id: 27139 reply_id: 112118[/import]

Help! Been rewriting this loop over and over. Its still a mess. Been testing on both the simulator and device. Heres a snippet:

  
 local function loadCoups()  
 for i=2, #myTable do  
  
 function networkListener( event )  
 if ( event.isError ) then  
 print ( "Network error - download failed" )  
 else  
 myLogos[i] = display.newImage(myTable[i].logo, system.DocumentsDirectory,-110,120 )  
 coupGroup:insert(myLogos[i])  
 myLogos[i]:translate(100,(i-2) \* 102)  
 myLogos[i]:scale(.75,.75)  
  
 detailThumbs[i] = display.newImage(myTable[i].detail, system.DocumentsDirectory,390,130 )  
 detailGroup:insert(detailThumbs[i])  
 detailThumbs[i].alpha=0  
 detailThumbs[i]:scale(.95,.95)  
 end  
 -- print ( "RESPONSE: " .. event.response )  
 end  
 network.download( "http://thisiskel.com/app/"..myTable[i].logo, "GET", networkListener, myTable[i].logo, system.DocumentsDirectory )  
 network.download( "http://thisiskel.com/app/"..myTable[i].detail, "GET", networkListener, myTable[i].detail, system.DocumentsDirectory )  
  
 function ImageClicked(event)  
 clicked = i  
 local t = event.target  
 function bounce()  
 transition.to( t, { time=200, xScale=.75, yScale=.75, transition=easing.OutExpo, onComplete=showContent} )  
 end  
 transition.to( t, { time=150, xScale=.55, yScale=.55, transition=easing.outExpo, onComplete=bounce} )  
 end  
  
 badgeSprite = myTable[i].badge  
  
 local sf = loqsprite.newFactory(badgeSprite)  
 loqsprite.setMinPrepare(true)  
 local badgeSprite = sf:newSpriteGroup()  
  
 function playBadge()  
 badgeSprite.alpha=1  
 badgeSprite:play()  
 end  
  
 badgeSprite.timeScale = 2   
 timer.performWithDelay(3000, playBadge )  
 badgeSprite.timeScale = 2  
 loqsprite.setMinPrepare(false)   
  
 badgeSprite.alpha=0  
 badgeSprite.x = \_W/2 -70  
 badgeSprite.y = \_H/2 -60  
 badgeSprite.xScale = .70  
 badgeSprite.yScale = .70  
 badgeSprite:translate(100,(i-2) \* 102)  
 badgeSprite:addEventListener("tap", ImageClicked)  
 badgeSprite:setReferencePoint(display.CenterReferencePoint);  
  
 local square = display.newRect(3,132,313,100)   
 square:translate(0,(i-2) \* 102)  
 coupGroup:insert(square)   
  
 local arrow = display.newImage("images/g\_coup\_arrow.png",0,140);  
 arrow:scale(.75,.75)  
 arrow:translate(250,(i-2) \* 102)  
 coupGroup:insert(arrow)   
 coupGroup:insert(badgeSprite)  
  
 local coupHeight = coupGroup.height  
 coupGroup.y=-135 - coupHeight  
 --print(coupHeight)  
 --print(coupGroup.contentHeight)  
 end  
 end  
  

Two things going wrong and i cant seem to fix…

  1. The initial launch of the app loads the json in my (main.jua), but my for loop doesnt fire! Nothing gets loaded. BUT after i refresh the simulator or relaunch the app on iPhone, it loads all the images. Why is it hanging?

i tried a few shots in the dark with something like:

  
coupsLoaded = false  
 initVars()  
 loadCoups()  
 local function checkIfLoaded()  
 print("checking")  
 for i=2, #myTable do  
 print(myTable[i].logo)  
 print(myTable[i].detail)  
 if fileExists(myTable[i].logo) and fileExists(myTable[i].detail) then   
 print(coupsLoaded)  
 coupsLoaded = true  
 end  
 if coupsLoaded ==true then  
 Runtime:removeEventListener( "enterFrame", checkIfLoaded )   
 initInterface()  
 end  
 end   
 end  
  
Runtime:addEventListener( "enterFrame", checkIfLoaded )  
  

but it just hangs on print("checking") and doesnt make it any farther down the function.

then theres…

  1. DEVICE ONLY, after refreshing the app - the last two buttons dont build. Say i have 8 items to load, only six will display. Whats am i doing wrong?

I truly appreciate all the help! [import]uid: 88004 topic_id: 27139 reply_id: 112920[/import]

Just looking at the last block of code, the one thing that would cause “checking” to print then nothing else is if #myTable (the number of items in the myTable array is less than 2. So if myTable is empty or it only has one item, that for loop will not execute because the condition of the for loop is no longer true.

The top snippet doesn’t really show how myTable is created or populated. The top snippet is also dependent on #myTable having at least two items in the table. [import]uid: 19626 topic_id: 27139 reply_id: 112989[/import]

I appreciate you taking a peak at this over the weekend robmiracle. myTable gets loaded with some json in my main.lua

I see the json download into my documents folder at launch.

snippet from my main.lua:

 function loadTable(filename)  
 path = system.pathForFile( filename, system.DocumentsDirectory)  
 contents = ""  
 myTable = {}  
 file = io.open( path, "r" )  
 if file then  
 contents = file:read( "\*a" )  
 myTable = json.decode(contents);   
 io.close( file )  
 return myTable   
 end  
 return nil  
 end  
  
 local function networkListener( event )  
 if ( event.isError ) then  
 --print ( "Network error - download failed" )  
 else  
 --print(event.response)  
 end  
 end  
  
 network.download( "http://www.thisiskel.com/deals.json", "GET", networkListener, "deals.json", system.DocumentsDirectory )  
  
 loadTable("deals.json")   

You can see that its pretty straight forward. I run the main.lua, it loads the json and dumps values into myTable. So the table items are already there and are ready to be accessed when the for loop is supposed to kick in.

Im running director as well. Would the fact that my for loop is located in another scene be the problem? should i just paste all my code?

[import]uid: 88004 topic_id: 27139 reply_id: 113079[/import]

I think I may see your problem.

network.download() is an asynchronous call. That means that it fires off in the background and returns immediately to your app. Your loadTable() function executes immediately, probably even before the network.download() has even had a chance to open it’s socket. So there is a very high (99.9%) chance that JSON file has not downloaded when you try to open it.

The correct procedure here is to take advantage of that “networkListener” call. When the network.request() completes it calls networkListener() and that is the time for your loadTable() call.

Try this!

function loadTable(filename)  
 path = system.pathForFile( filename, system.DocumentsDirectory)  
 contents = ""  
 myTable = {}  
 file = io.open( path, "r" )  
 if file then  
 contents = file:read( "\*a" )  
 myTable = json.decode(contents);   
 io.close( file )  
 return myTable   
 end  
 return nil  
end  
   
local function networkListener( event )  
 if ( event.isError ) then  
 --print ( "Network error - download failed" )  
 else  
 loadTable("deals.json")   
 end  
end  
   
network.download( "http://www.thisiskel.com/deals.json", "GET", networkListener, "deals.json", system.DocumentsDirectory )  
  

[import]uid: 19626 topic_id: 27139 reply_id: 113085[/import]

holy. cow. thanks Robmiracle! I feel like i get so close every time but just cant crack that last part. I truly appreciate it.

Just one more question on this code i swear! My for loop is still not completely loading all my items from myTable. Currently i have 7 items in myTable and when i test on my device, the last two dont load. Works fine in the simulator.

Since things arent really jiving between the simulator and my device, i cant really figure out what the issue is.

I get a vague director error that reads:
"director ERROR: Failed to execute new( params ) function on ‘deals’

Most of the code still executes. sans the last two table items.

My guess would be that i have “coupGroup” (where my all my table items are loading into) and im inserting that group into directors “localGroup”. im not sure.

Again, thank you for your help. You have really made this learning process an enjoyable one! [import]uid: 88004 topic_id: 27139 reply_id: 113130[/import]

hmm… made a bit of progress. I fixed the director error. Also took Robmiracles tip on executing the build in the “network listener” call. But now, the first 5 items load properly, and it skips the 6th item, and builds the 7th item. haha now im completely confused.

Anyone got some ideas? I know its something in this messy chunk of code. I just cant figure it out!

  
 local function loadCoups()  
 for i=2, #myTable do  
 clicked = i  
 local function ImageClicked(event)  
 clicked = i  
 local t = event.target  
 local function bounce()  
 transition.to( t, { time=200, xScale=.75, yScale=.75, transition=easing.OutExpo, onComplete=showContent} )  
 end  
 transition.to( t, { time=150, xScale=.55, yScale=.55, transition=easing.outExpo, onComplete=bounce} )  
 end  
 local function networkListener( event )  
 if ( event.isError ) then  
 print ( "Network error - download failed" )  
 else  
 local badgeSprite = myTable[i].badge  
 local sf = loqsprite.newFactory(badgeSprite)  
 loqsprite.setMinPrepare(true)  
 local badgeSprite = sf:newSpriteGroup()  
  
 local function playBadge()  
 badgeSprite.alpha=1  
 badgeSprite:play()  
 end  
  
 badgeSprite.timeScale = 2   
 timer.performWithDelay(3000, playBadge )  
 badgeSprite.timeScale = 2  
 loqsprite.setMinPrepare(true)   
  
 badgeSprite.alpha=0  
 badgeSprite.x = \_W/2 -70  
 badgeSprite.y = \_H/2 -60  
 badgeSprite.xScale = .70  
 badgeSprite.yScale = .70  
 badgeSprite:translate(100,(i-2) \* 102)  
 badgeSprite:addEventListener("tap", ImageClicked)  
 badgeSprite:setReferencePoint(display.CenterReferencePoint);  
  
 local square = display.newRect(3,132,313,100)   
 square:translate(0,(i-2) \* 102)  
 coupGroup:insert(square)   
  
 local arrow = display.newImage("images/g\_coup\_arrow.png",0,140);  
 arrow:scale(.75,.75)  
 arrow:translate(250,(i-2) \* 102)  
 coupGroup:insert(arrow)   
 coupGroup:insert(badgeSprite)  
  
 myLogos[i] = display.newImage(myTable[i].logo, system.DocumentsDirectory,-110,120 )  
 myLogos[i]:translate(100,(i-2) \* 102)  
 myLogos[i]:scale(.75,.75)  
 coupGroup:insert(myLogos[i])  
  
 detailThumbs[i] = display.newImage(myTable[i].detail, system.DocumentsDirectory,390,130 )  
 detailGroup:insert(detailThumbs[i])  
 detailThumbs[i].alpha=0  
 detailThumbs[i]:scale(.95,.95)  
  
 local coupHeight = coupGroup.height  
 coupGroup.y=-135 - coupHeight   
 end  
 end   
 network.download( "http://thisiskel.com/app/"..myTable[i].logo, "GET", networkListener, myTable[i].logo, system.DocumentsDirectory )   
 network.download( "http://thisiskel.com/app/"..myTable[i].detail, "GET", networkListener, myTable[i].detail, system.DocumentsDirectory )  
 end  
 end  

I also cant figure out how to break these nested functions out of the loop. Seems pretty inefficient (and looks ugly) [import]uid: 88004 topic_id: 27139 reply_id: 113284[/import]

I think you are running into a problem I helped someone else with.

Your loop to load the images is firing off the requests and as I said before they are asynchronous which not only means the for loop finishes before the requests are done, there is no guarantee they will happen in any order. Asynchronous means not synchronous so there is no promises they will be in sync.

Also the variable “i” is local to your for loop only. Since that loop will end before your downloads finish (or even begin), “i” can’t be guaranteed to be any value inside that networListener function, nor is it even passed in.

The problem that we ran into in the other post, is there is no way to pass the index number of the image to the event listener in any reasonable manner.

What we ended up working out was to put the index number (your “i” value in your for loop) as part of the file name of the image that’s saved locally. Then in the networkListener function, you get access to the file name in the event.url value and you can then use string.gsub() to parse out the index number from the file name.
[import]uid: 19626 topic_id: 27139 reply_id: 113355[/import]

Thanks for the info robmiracle!

I can kind of understand what youre explaining. Hardcode the value rather than using i to determine which number to call.

Could you point me to the post you’re referring to? I’d like to see an example. using string.gsub() is confusing me [import]uid: 88004 topic_id: 27139 reply_id: 113402[/import]

Update! I sort of found a work around the issue. Rather than using a for loop to both download and build my interface, I use it only to download to my documents directory. THEN i go through and build the list.

After kludging my way through this, I discovered a reason why my loop was populating strangely - or not working at all. My spriteloq syntax is incorrect! I’ve posted on that thread.

Anyway, heres the code if youre curious:
[lua] local function buildCoupons()
iNum = iNum + 1
print("—iNum is "…iNum)
print("myTable is "…#myTable)

local square = display.newRect(3,132,313,100)
square:translate(0,(iNum-2) * 102)
coupGroup:insert(square)

local arrow = display.newImage(“images/g_coup_arrow.png”,0,140);
arrow:scale(.75,.75)
arrow:translate(250,(iNum-2) * 102)
coupGroup:insert(arrow)

myLogos[iNum] = display.newImage(myTable[iNum].logo, system.DocumentsDirectory,-110,120 )
myLogos[iNum]:translate(100,(iNum-2) * 102)
myLogos[iNum]:scale(.75,.75)
coupGroup:insert(myLogos[iNum])

detailThumbs[iNum] = display.newImage(myTable[iNum].detail, system.DocumentsDirectory,390,130 )
detailGroup:insert(detailThumbs[iNum])
detailThumbs[iNum].alpha=0
detailThumbs[iNum]:scale(.95,.95)

local badgeSprite = loqsprite.newFactory(myTable[iNum].badge)
local badge = badgeSprite:newSpriteGroup()

local coupHeight = coupGroup.height
–coupGroup.y=-135 - coupHeight
coupGroup.y=-350

if iNum == #myTable then
print(“initialize”)
–initInterface()
end
end

function populateCoupons()
print(“build”)
timer.performWithDelay(100, buildCoupons, #myTable-1)
end


– Load Images

local function checkDirectory()
for i = 1, #myTable do
print(myTable[i].logo)
print(myTable[i].detail)
if fileExists(myTable[i].logo) then
logosLoaded = true
end
if fileExists(myTable[i].detail) then
thumbsLoaded = true
end
end
end

local function checkIfLoaded()
checkDirectory()
if logosLoaded == true and thumbsLoaded == true then
timer.cancel(checker)

populateCoupons()
end
end
checker = timer.performWithDelay(500, checkIfLoaded,0)

local function networkListener( event )
if ( event.isError ) then
print ( “Network error - download failed” )
else
print(“loading complete”)
end
end

function downloadAllImages()
for i=1, #myTable do
network.download( “http://thisiskel.com/app/”…myTable[i].logo, “GET”, networkListener, myTable[i].logo, system.DocumentsDirectory )
network.download( “http://thisiskel.com/app/”…myTable[i].detail, “GET”, networkListener, myTable[i].detail, system.DocumentsDirectory )
end
end

downloadAllImages()[/lua]

hey @robmiracle, if you could still point me to that other post you mentioned when you have a chance, i’d really appreciate it! Im curious to how the pros do it! [import]uid: 88004 topic_id: 27139 reply_id: 113446[/import]