Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Removing all objects, when moving to another scene by a click
Started by GO4IT May 15 2019 03:36 AM

17 replies to this topic
api remove objects scene management two functions event.target

Best Answer GO4IT , 17 May 2019 - 02:53 AM

Thank you very much for the detailed answer. It is so kind of you!

 

Firstly, I managed to remove two images when one image is clicked and moved to a new scene, using myGroup:insert( event.target ).

Secondly, I managed to refactor the code and make it shorter, taking your suggestion A). So, now the API call function is created.

Now this part looks like:

local composer = require( "composer" )
local json = require( "json" )

local function apicall (newpage, lotkeyword, lotyear, xbutton, ximage, filenumber)
    local function networkListener( event )
    local res = json.prettify( event.response )
    local decoded = json.decode( res )
    if ( event.isError ) then
        print( "--Network error-- ", ( res ) )
    else
        print( "Results: " .. ( res ) )
        local rights = decoded.items[lot20].rights[1]
        local year = decoded.items[lot20].year[1]
        local edmPreview = decoded.items[lot20].edmPreview[1]

        -- Display image from web
        local function networkListener1image( event )
            if ( event.isError ) then
                print ( "Network error - download failed" )
            else
                myGroup:insert( event.target )
                event.target.alpha = 0
                transition.to( event.target, { alpha = 1.0 } )
            end

            print ( "event.response.fullPath: ", event.response.fullPath )
            print ( "event.response.filename: ", event.response.filename )
            print ( "event.response.baseDirectory: ", event.response.baseDirectory )

            --Click an image and go to next scene (remove all objects)
            local function onTap(event)
                event.target:removeSelf()
                composer.gotoScene(newpage,{time=800,effect="crossFade"})
            end
            local myImage = event.target
            myImage:addEventListener("tap", onTap)
            return myImage
        end
        local loadedimage = display.loadRemoteImage(edmPreview, "GET", networkListener1image, "image"..filenumber..".png", system.TemporaryDirectory, ximage, display.contentCenterY )
            return loadedImage
        end
    end
    local yourImage    = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword.."&qf=proxy_edm_year:"..lotyear.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params)
    return yourImage
end

apicall("pageA", lotkeyword1, lotyear1, 50, 200, 1)
apicall("pageB", lotkeyword2, lotyear2, 800, 900, 2)

Those are exactly what I needed. Excellent.

Apart from that, the only thing I didn't still understand well, is this part:

 

 

If you return variables like this and have your network request set to some handle, then you can later access that variable through the handle.

 

Can you show me a quick example? For example, how can I specify the position of the image (myImage) outside the function (networkListener(event))? I am guessing, I would use "params" to manipulate the image, after the following code, but not sure...

local yourImage = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword1.."&qf=proxy_edm_year:"..lotyear1.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params)

[TOPIC CONTROLS]
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

GO4IT

[GLOBAL: userInfoPane.html]
GO4IT
  • Observer

  • 15 posts
  • Corona SDK

I have a problem removing objects (images) in my app. I don't know how to fix this.

 

I create and display two images based on two API calls. The idea is: when one image is clicked, all objects should be removed and the user goes to a new page/scene. Depending on the image clicked, the new page is different.

 

So far, I can remove the image the user clicked and move to another page (event.target:removeSelf()). But the other image cannot be removed. The easiest solution, I think, is to put the two images (each event.target of each API call) into one sceneGroup and remove it, when one of the two images is clicked, but I don't know how to do it. Or, maybe there is a simpler approach to manipulate two images outside two API call functions.

 

Do you have any good idea?

--1st API call to fetch 1st image
    local json = require( "json" )
    local function networkListener( event )
        local res = json.prettify( event.response )
        local decoded = json.decode( res )
        if ( event.isError ) then
            print( "--Network error-- ", ( res ) )
        else
            local edmPreview = decoded.items[lot20].edmPreview[1]

            -- Display image from web
            local function networkListener1image( event )
                if ( event.isError ) then
                    print ( "Network error - download failed" )
                else
                    event.target.alpha = 0
                    transition.to( event.target, { alpha = 1.0 } )
                end

                print ( "event.response.fullPath: ", event.response.fullPath )
                print ( "event.response.filename: ", event.response.filename )
                print ( "event.response.baseDirectory: ", event.response.baseDirectory )

                --Set an event to move to another scene when the image object is clicked
                local function onTap(event)
                    event.target:removeSelf()
                    composer.gotoScene("NewPageA",{time=800,effect="crossFade"})
                end
                local myImage = event.target
                myImage:addEventListener("tap", onTap)
            end

            local loadedimage = display.loadRemoteImage(edmPreview, "GET", networkListener1image, "image1.png", system.TemporaryDirectory, 200, display.contentCenterY )

        end
    end
    network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword1.."&qf=proxy_edm_year:"..lotyear1.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params)

-- 2nd API call to fetch 2nd image
local json2 = require( "json" )
local function networkListener2( event )
    local res2 = json.prettify( event.response )
    local decoded2 = json2.decode( res2 )
        if ( event.isError ) then
                print( "--Network error-- ", ( res2 ) )
        else
        local edmPreview2 = decoded2.items[lot20].edmPreview[1]

        -- Display image from web
        local function networkListener2image( event )
                if ( event.isError ) then
                        print ( "Network error - download failed" )
                else
                        event.target.alpha = 0
                        transition.to( event.target, { alpha = 1.0 } )
                end

                print ( "event.response.fullPath: ", event.response.fullPath )
                print ( "event.response.filename: ", event.response.filename )
                print ( "event.response.baseDirectory: ", event.response.baseDirectory )

                --Set an event to move to another scene when the image object is clicked
                local function onTap(event)
                        event.target:removeSelf()
                        composer.gotoScene("NewPageB",{time=800,effect="crossFade"})
                end
                local myImage = event.target
                myImage:addEventListener("tap", onTap)
        end

        local loadedimage2 = display.loadRemoteImage(edmPreview2, "GET", networkListener2image, "image2.png", system.TemporaryDirectory, 900, display.contentCenterY )

        end
end
    network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword2.."&qf=proxy_edm_year:"..lotyear2.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener2, params)

The above code is of course inserted in the scene template below:

-- -----------------------------------------------------------------------------------
-- Scene event functions
-- -----------------------------------------------------------------------------------

-- create()
-- :create( event ) indicates that this function will be associated with the Composer create scene event and that a table of data that we reference with event will be passed to the function.
function scene:create(event)
    --This creates a local reference to the scene's view group, automatically created by Composer, which should contain all of the display objects used in the scene. Essentially, any display object which should be part of the scene must be inserted into the scene's view group, and that group is referenced by sceneGroup inside each of the template's default scene: functions.
    local sceneGroup = self.view
        -- Code here runs when the scene is first created but has not yet appeared on screen.

end

-- show()
function scene:show(event)

    local sceneGroup = self.view
    local phase = event.phase

    if ( phase == "will" ) then
        -- Code here runs when the scene is still off screen (but is about to come on screen)

    elseif ( phase == "did" ) then
        -- Start the music loop=-1 means indefinete loop
        audio.play(menuMusic,{channel=1,loop=-1})
        -- Code here runs when the scene is entirely on screen

    end
end


-- hide()
function scene:hide( event )

    local sceneGroup = self.view
    local phase = event.phase

    if ( phase == "will" ) then
        -- Code here runs when the scene is on screen (but is about to go off screen)

    elseif ( phase == "did" ) then
        -- Stop the music
        audio.stop(1)
        -- Code here runs immediately after the scene goes entirely off screen

    end
end


-- destroy()
function scene:destroy( event )

    local sceneGroup = self.view

    -- We effectively release the memory taken up by the audio file
    audio.dispose(menuMusic)
    -- Code here runs prior to the removal of scene's view

end


-- -----------------------------------------------------------------------------------
-- Scene event function listeners
-- -----------------------------------------------------------------------------------
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )
-- -----------------------------------------------------------------------------------

return scene


[TOPIC: post.html]
#2

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 894 posts
  • Corona SDK

You seem to be using composer, which means that if you add all of your display objects into a display group (and then the sceneGroup), then those display objects will be removed on scene change. There's also the matter of scope, where you create the images within those functions without returning anything. This means that you won't have access to them outside of the functions where they are created.

Also, I don't think that there is ever any reason to load the same library twice in one file, i.e. your json and json2. You should also avoid duplicating code like that since those functions literally do the same thing.


  • GO4IT likes this

[TOPIC: post.html]
#3

GO4IT

[GLOBAL: userInfoPane.html]
GO4IT
  • Observer

  • 15 posts
  • Corona SDK

XeduR @Spyric Thank you very much for some tips!

Yes, that's basically what I am struggling with.

 

I try to understand what I should do now. Maybe, I can forget about repeating json and json2 for the time being, to simplify the logic.

The first problem is how to process the image outside the scope of the function of a API query. I should return something, right?

Where can I do it, and how to retrieve the returned object in order to be put in a sceneGroup outside the function?

 

After that, I have to think of how to organise json and json2, because they are not the same. There are two different API queries and results (i.e. images) are different. So, I hope I can avoid repetition as much as possible, but I am still not very sure how to do it...

 

Then, two objects can be put into sceneGroup and delete them all together. The last question is how to make delete happen. Two objects should be removed, when one of the two objects is clicked. How to invoke that? Currently, the scope of one function (ie json) cannot see the scope of the other function (json2), so this sounds a bit tricky to me to implement.

 

Do you know a concrete (whole or partial) solution for those issues?



[TOPIC: post.html]
#4

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 894 posts
  • Corona SDK

So, to begin with, when you require libraries or other external files/modules, e.g.

local json = require("json")

This means that you can now call functions by referring to the "json" variable as in json.decode(), json.decodeFile(), json.encode() and json.prettify(). This means that creating another variable called "json2" simply creates a duplicate without providing any additional use. This is why I am not sure if there is ever a reason to require the same library/module more than once. You typically want to require all libraries like this (only once) at the beginning of your code file so that you can freely use them later on.


Now, with the functions.
 

local json = require( "json" ) -- just once is enough!
-- local composer = require( "composer" ) -- guessing you have this already required elsewhere in the file?

local myGroup = display.newGroup()
-- sceneGroup:insert( myGroup )
--[[
	if you add myGroup to your sceneGroup, then it, as well as all display objects
	inserted into mygroup will be removed on composer.gotoScene( "someScene" ).
]]--

local function networkListener( event )
    local res = json.prettify( event.response )
    local decoded = json.decode( res )
    if ( event.isError ) then
        print( "--Network error-- ", ( res ) )
    else
        local edmPreview = decoded.items[lot20].edmPreview[1]

        -- Display image from web
        local function networkListener1image( event )
            if ( event.isError ) then
                print ( "Network error - download failed" )
            else
				myGroup:insert( event.target ) -- the file was downloaded, so add it to myGroup
                event.target.alpha = 0
                transition.to( event.target, { alpha = 1.0 } )
            end

            print ( "event.response.fullPath: ", event.response.fullPath )
            print ( "event.response.filename: ", event.response.filename )
            print ( "event.response.baseDirectory: ", event.response.baseDirectory )

            --Set an event to move to another scene when the image object is clicked
            local function onTap(event)
                event.target:removeSelf()
                composer.gotoScene("NewPageA",{time=800,effect="crossFade"})
            end
            local myImage = event.target
            myImage:addEventListener("tap", onTap)
			return myImage -- return myImage to the above function
        end

        local loadedimage = display.loadRemoteImage(edmPreview, "GET", networkListener1image, "image1.png", system.TemporaryDirectory, 200, display.contentCenterY )
		return loadedImage -- return loadedImage to where the function was called
    end
end

local yourImage = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword1.."&qf=proxy_edm_year:"..lotyear1.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params)
-- now you can manipulate yourImage outside of the function, but remember that since you are working with asynchronous functions so the image will take time to download

All you really need to do to return something from a function is to write "return [var]", e.g. "return myImage" at the end of the function. If you return variables like this and have your network request set to some handle, then you can later access that variable through the handle.

I only skimmed through your code quickly, so there may be some mistakes. But, concerning your two functions, there seems to only be slight differences. What you can do is A ) pass some values to the function, e.g. "whatScene", that you could set to "NewPageA" or "NewPageB" depending on what you need. This way, you can use the same function but just include an extra parameter. Alternatively, B ) you could have some variable that is declared before the function. So, basically the standard Lua function stuff, i.e.
 

-- a)
local function printVarA( var )
	print( var )
end
printVar( "a" ) -- outputs: a
printVar( "b" ) -- outputs: b

-- b)
local myVar
local function printVarB()
	print( myVar )
end
myvar = "a"
printVar() -- outputs: a
myvar = "b"
printVar() -- outputs: b

Finally, if you use composer to move between scenes, then any display objects or display groups that have been inserted into the sceneGroup will automatically be removed upon scene change. If you don't use (or don't want to use) composer, then you can also just remove the display group that you've inserted the objects into and they will be removed.


  • GO4IT likes this

[TOPIC: post.html]
#5

GO4IT

[GLOBAL: userInfoPane.html]
GO4IT
  • Observer

  • 15 posts
  • Corona SDK

  Best Answer

Thank you very much for the detailed answer. It is so kind of you!

 

Firstly, I managed to remove two images when one image is clicked and moved to a new scene, using myGroup:insert( event.target ).

Secondly, I managed to refactor the code and make it shorter, taking your suggestion A). So, now the API call function is created.

Now this part looks like:

local composer = require( "composer" )
local json = require( "json" )

local function apicall (newpage, lotkeyword, lotyear, xbutton, ximage, filenumber)
    local function networkListener( event )
    local res = json.prettify( event.response )
    local decoded = json.decode( res )
    if ( event.isError ) then
        print( "--Network error-- ", ( res ) )
    else
        print( "Results: " .. ( res ) )
        local rights = decoded.items[lot20].rights[1]
        local year = decoded.items[lot20].year[1]
        local edmPreview = decoded.items[lot20].edmPreview[1]

        -- Display image from web
        local function networkListener1image( event )
            if ( event.isError ) then
                print ( "Network error - download failed" )
            else
                myGroup:insert( event.target )
                event.target.alpha = 0
                transition.to( event.target, { alpha = 1.0 } )
            end

            print ( "event.response.fullPath: ", event.response.fullPath )
            print ( "event.response.filename: ", event.response.filename )
            print ( "event.response.baseDirectory: ", event.response.baseDirectory )

            --Click an image and go to next scene (remove all objects)
            local function onTap(event)
                event.target:removeSelf()
                composer.gotoScene(newpage,{time=800,effect="crossFade"})
            end
            local myImage = event.target
            myImage:addEventListener("tap", onTap)
            return myImage
        end
        local loadedimage = display.loadRemoteImage(edmPreview, "GET", networkListener1image, "image"..filenumber..".png", system.TemporaryDirectory, ximage, display.contentCenterY )
            return loadedImage
        end
    end
    local yourImage    = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword.."&qf=proxy_edm_year:"..lotyear.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params)
    return yourImage
end

apicall("pageA", lotkeyword1, lotyear1, 50, 200, 1)
apicall("pageB", lotkeyword2, lotyear2, 800, 900, 2)

Those are exactly what I needed. Excellent.

Apart from that, the only thing I didn't still understand well, is this part:

 

 

If you return variables like this and have your network request set to some handle, then you can later access that variable through the handle.

 

Can you show me a quick example? For example, how can I specify the position of the image (myImage) outside the function (networkListener(event))? I am guessing, I would use "params" to manipulate the image, after the following code, but not sure...

local yourImage = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword1.."&qf=proxy_edm_year:"..lotyear1.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params)


[TOPIC: post.html]
#6

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 894 posts
  • Corona SDK

local function newObject( x, y, width, height )
	local object = display.newRect( x, y, width, height )
	object:setFillColor( 1, 0, 0 )
	return object
end

local object = newObject( 160, 240, 80, 20 )
object.x = object.x+40
object.y = 200

This would be the most basic way of doing that. However, as I pointed out at the very end of my previous sample code, that network.request is an asynchronous function, which means that if you tried to immediately move the display object, you'd probably crash the app. This is because the file would not yet have been downloaded and so the image wouldn't exist yet (i.e. it'd be nil).

So, this is the way to do it, but you need to leave manipulating the object until it has been downloaded. In cases like this, you can always check if it exists before trying anything to avoid crashes, e.g. "if object then", etc.



[TOPIC: post.html]
#7

GO4IT

[GLOBAL: userInfoPane.html]
GO4IT
  • Observer

  • 15 posts
  • Corona SDK

Thank you XeduR @Spyric again for instructing me very well. :)

 

But, maybe I confused you a bit. I know how to the position of the image in general.

My question was about how to access the object returned from the functions (i.e. variables: yourImage or loadedImage) outside the function. Then, I simply replace it with the "object" variable in your last code, right?

 

The question may be pretty basic and I did a few trials and errors, but I did not figure it out by myself. I'm too beginner... :(

I always get an error "attempt to call field 'loadedImage' (a nil value)", if I do something like

local function newObject( x, y, width, height, loadedImage)
    local object = display.loadedImage( x, y, width, height )
    object:setFillColor( 1, 0, 0 )
    return object
end
local object = newObject( 160, 240, 80, 20, loadedImage )

I totally understood the asynchronous actions, and yes, I have to do something with that to avoid getting nil. Cheers!



[TOPIC: post.html]
#8

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 894 posts
  • Corona SDK

You get the aforementioned error because of how asynchronous functions work (and how I showed you in the code how to return variables from functions). With asynchronous functions, you just need to wait for them to finish before you can perform any operations on the variables you return from the functions (i.e. wait for the download or some other process to finish). If you try to change the x position of a display object before it has been created, it won't yet exist and that is why it crashes.
 


  • GO4IT likes this

[TOPIC: post.html]
#9

nick_sherman

[GLOBAL: userInfoPane.html]
nick_sherman
  • Corona Geek

  • 1,820 posts
  • Corona SDK

display.loadedImage does not exist, that's why you're getting the nil error there.


  • GO4IT likes this

[TOPIC: post.html]
#10

GO4IT

[GLOBAL: userInfoPane.html]
GO4IT
  • Observer

  • 15 posts
  • Corona SDK

Ah Ok, I see. asynchronous is tricky!

I almost always get nil, so it seems to be impossible to access and manipulate the image so quickly, unless I write more code to wait for the image.

 

In that case, I think the best way would be to use the saved images (image1.png) from the function, and manipulate it.

So, my solution would be to add the following after/outside the function (in this case change the colour of the image), and it worked.

local catImage = display.newImage( "image1.png", system.TemporaryDirectory, 0, 0 )
catImage:setFillColor( 1, 0, 0 )

Cheers!



[TOPIC: post.html]
#11

GO4IT

[GLOBAL: userInfoPane.html]
GO4IT
  • Observer

  • 15 posts
  • Corona SDK

With the above example, I can access images, but another problem is I have to think how to get other data than images (textual data from API calls), because I need to display them as well. But that's another story...

 

Removing and/or maniplulating objects/images (as suggested in the question title) is accomplished. Thank you so much all!



[TOPIC: post.html]
#12

nick_sherman

[GLOBAL: userInfoPane.html]
nick_sherman
  • Corona Geek

  • 1,820 posts
  • Corona SDK

I think you're still missing the point of asynchronous. Can you guarantee that "image1.png" will exist at the time you try to display it? I'm not sure you can, if there are network issues or it's a big file. You might also end up displaying the image twice.

 

If I understand correctly, you want to load an image remotely, and when leaving the scene have that image be removed. In order to do that, you either need to have a handle on the image so you can remove it manually, or add the image to the scene.view group.

 

When you call display.loadRemoteImage, this returns nothing immediately. You need to wait until the networkListener function is fired upon successful download, and then event.target is the handle to the image. You can then store this handle in a variable that is accessible outside the listener. I've modified the corona API example:

 

local myDownloadedImage  -- I am currently nil, but I have been forward-declared so I can be populated later and accessed from anywhere in this lua file
local theImageWasDownloaded = false
 
local function networkListener( event )
 
    if ( event.isError ) then
        print ( "Network error - download failed" )
    else
        myDownloadedImage = event.target -- now I'm not nil
        myDownloadedImage:setFillColor(1,0,0)
        scene.view:insert(myDownloadedImage) -- now Composer will automatically clean me up when we leave the scene
    end
end
 
display.loadRemoteImage( "http://coronalabs.com/images/coronalogogrey.png", "GET", networkListener, "coronalogogrey.png", system.TemporaryDirectory, 50, 50 )
 
print (myDownloadedImage) -- I am almost certainly still nil here. If I'm not, it's not guaranteed.
 
local gameLoop = function ()  -- I will run once a frame
  if (myDownloadedImage ~= nil and theImageWasDownloaded == false) then
   
    theImageWasDownloaded = true
   print ("myDownloadedImage is no longer nil. I can now be moved, coloured, resized, removed etc")
  end
end
 
Runtime:addEventListener("enterFrame",gameLoop)
 


  • GO4IT likes this

[TOPIC: post.html]
#13

GO4IT

[GLOBAL: userInfoPane.html]
GO4IT
  • Observer

  • 15 posts
  • Corona SDK

Thank you very much @nick_sherman

 

Ok, so, saving image as image1.png is not good enough to ensure the existence of the image, right?  :( 

If I understand your code correctly, the whole situation would now look like:

local composer = require( "composer" )
local json = require( "json" )

local myDownloadedImage1
local myDownloadedImage2
local theImageWasDownloaded = false

local function apicall (newpage, lotkeyword, lotyear, xbutton, ximage, filenumber, myDownloadedImage)
    local function networkListener( event )
    local res = json.prettify( event.response )
    local decoded = json.decode( res )
    if ( event.isError ) then
        print( "--Network error-- ", ( res ) )
    else
        print( "Results: " .. ( res ) )
        local rights = decoded.items[lot20].rights[1]
        local year = decoded.items[lot20].year[1]
        local edmPreview = decoded.items[lot20].edmPreview[1]

        -- Display image from web
        local function networkListener1image( event )
            if ( event.isError ) then
                print ( "Network error - download failed" )
            else
                myDownloadedImage.alpha = 0
                transition.to( myDownloadedImage, { alpha = 1.0 } )
                myDownloadedImage = event.target 
                scene.view:insert(myDownloadedImage) 
            end

            print ( "event.response.fullPath: ", event.response.fullPath )
            print ( "event.response.filename: ", event.response.filename )
            print ( "event.response.baseDirectory: ", event.response.baseDirectory )

            --Click an image and go to next scene (remove all objects)
            local function onTap(event)
                myDownloadedImage:removeSelf()
                composer.gotoScene(newpage,{time=800,effect="crossFade"})
            end
            myDownloadedImage:addEventListener("tap", onTap)
        end
        local loadedimage = display.loadRemoteImage(edmPreview, "GET", networkListener1image, "image"..filenumber..".png", system.TemporaryDirectory, ximage, display.contentCenterY )
        end
    end
    local yourImage    = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword.."&qf=proxy_edm_year:"..lotyear.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params)
end

apicall("pageA", lotkeyword1, lotyear1, 50, 200, 1, myDownloadedImage1)

local gameLoop = function ()  
    if (myDownloadedImage ~= nil and theImageWasDownloaded == false) then
        theImageWasDownloaded = true
        print ("myDownloadedImage is no longer nil. I can now be moved, coloured, resized, removed etc")
        myDownloadedImage:setFillColor( 1, 0, 0 )
    end
end

Runtime:addEventListener("enterFrame",gameLoop)

apicall("pageB", lotkeyword2, lotyear2, 800, 900, 2, myDownloadedImage2)

local gameLoop = function () 
    if (myDownloadedImage ~= nil and theImageWasDownloaded == false) then
        theImageWasDownloaded = true
        print ("myDownloadedImage is no longer nil. I can now be moved, coloured, resized, removed etc")
        myDownloadedImage:setFillColor( 1, 0, 0 )
    end
end

Runtime:addEventListener("enterFrame",gameLoop)
 

The gameLoop function is repeated, because I have two images from two functions. It could be refactored to reduce the code, but theoretically this would work (more or less), no?

A lot of checking to create the right code! If this is still wrong, perhaps I should cry and give up Lua :D

 

It seems good thing is I could use the same approach to check the availability of the texts, fetched from API calls, outside the API

functions.



[TOPIC: post.html]
#14

nick_sherman

[GLOBAL: userInfoPane.html]
nick_sherman
  • Corona Geek

  • 1,820 posts
  • Corona SDK

You're getting there, a few mistakes though. Seems you might be trying to run before you can walk, I had built and published several games before I went anywhere near asynchronous programming and networking.

 

1) You are trying to access the alpha, and make a transition on the alpha of myDownloadedImage before it is set to anything. The first thing you should do is myDownloadedImage = event.target. 

 

2) You can't have two 'gameLoop' functions. You could have two with different names, but you probably don't even need them and if you did, one function to handle both scenarios would be fine. They were just there to illustrate the point that myDownloadedImage will be nil and then at some unknown point in the future, it won't be. 

 

3) The gameLoop functions as they are won't do anything, as they are looking for myDownloadedImage, which never exists in their scope. They need to check for myDownloadedImage1 and 2, which are passed into apicall and within that function, have the pseudonym of myDownloadedImage, but outside are still accessed by their individual names.

 

4) As you are adding myDownloadedImage to scene.view, you don't need to call removeSelf - that's the whole point. Composer will deal with it for you.  Although, your code doesn't include the basic Composer scene variables and listeners that allow this magic to happen (scene:create etc), have you got those as per this link? https://docs.coronalabs.com/api/library/composer/index.html


  • GO4IT likes this

[TOPIC: post.html]
#15

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,360 posts
  • Corona SDK

i'm late to this conversation, and won't attempt to directly answer, but my personal impression of the latest posted code was "oh my goodness!!"  :D

 

what's shown below is NOT a direct answer to your question, but may suggest a simpler structure for the LOOPING of asynchronous events (ie, set up a task list, start first request, then have the receiver call the requester until done, then callback to somewhere else)

 

 

fwiw, hth:

local downloadTaskList = {
  {
    url="http://www.google.com/logos/2012/eclipse12-hp.jpg",
    localFilename = "image1.jpg",
    screenX = display.contentCenterX,
    screenY = display.contentCenterY-200,
    downloadAttempted = false,
    downloadSucceeded = false,
  },
  {
    url="http://www.google.com/logos/doodles/2018/celebrating-garden-gnomes-6194737877876736.5-l.png",
    localFilename = "image2.png",
    screenX = display.contentCenterX,
    screenY = display.contentCenterY,
    downloadAttempted = false,
    downloadSucceeded = false,
  },
  {
    url="http://www.google.com/logos/doodles/2017/valentines-day-2017-day-4-5165155370401792-hp2x.jpg",
    localFilename = "image3.jpg",
    screenX = display.contentCenterX,
    screenY = display.contentCenterY+200,
    downloadAttempted = false,
    downloadSucceeded = false,
  },
}

local downloadTaskIndex = 1
local downloadTaskImages = {}
local downloadTaskOnComplete -- forward declare, to resolve scope
local downloadTaskListener -- forward declare, to resolve scope

local function downloadTaskRequester(onComplete)
  if (downloadTaskIndex > #downloadTaskList) then
    -- all done
    if (type(downloadTaskOnComplete)=="function") then
      downloadTaskOnComplete()
    end
  else
    -- still tasks left to do
    print("requesting task # ", downloadTaskIndex)
    local task = downloadTaskList[downloadTaskIndex]
    display.loadRemoteImage(task.url, "GET", downloadTaskListener, task.localFilename, system.TemporaryDirectory, task.screenX, task.screenY)
    task.downloadAttempted = true
  end
end

downloadTaskListener = function(event)
  print("receiving task # ", downloadTaskIndex, "isError:", event.isError, "response:", event.response)
  if (event.isError) then
    -- whatever, as appropriate, handle the error somehow - sorry, but you didn't get this image, deal with it
  else
    downloadTaskList[downloadTaskIndex].downloadSucceeded = true
    downloadTaskImages[downloadTaskIndex] = event.target
  end
  -- attempt next task
  downloadTaskIndex = downloadTaskIndex + 1
  downloadTaskRequester()
end

downloadTaskOnComplete = function()
  print("all download tasks completed")
  -- composer.gotoScene(...for example
end

downloadTaskRequester()


  • GO4IT likes this

[TOPIC: post.html]
#16

GO4IT

[GLOBAL: userInfoPane.html]
GO4IT
  • Observer

  • 15 posts
  • Corona SDK

Hi  @nick_sherman

 

You're getting there, a few mistakes though. Seems you might be trying to run before you can walk, I had built and published several games before I went anywhere near asynchronous programming and networking.

 

1) You are trying to access the alpha, and make a transition on the alpha of myDownloadedImage before it is set to anything. The first thing you should do is myDownloadedImage = event.target. 

 

2) You can't have two 'gameLoop' functions. You could have two with different names, but you probably don't even need them and if you did, one function to handle both scenarios would be fine. They were just there to illustrate the point that myDownloadedImage will be nil and then at some unknown point in the future, it won't be. 

 

3) The gameLoop functions as they are won't do anything, as they are looking for myDownloadedImage, which never exists in their scope. They need to check for myDownloadedImage1 and 2, which are passed into apicall and within that function, have the pseudonym of myDownloadedImage, but outside are still accessed by their individual names.

 

4) As you are adding myDownloadedImage to scene.view, you don't need to call removeSelf - that's the whole point. Composer will deal with it for you.  Although, your code doesn't include the basic Composer scene variables and listeners that allow this magic to happen (scene:create etc), have you got those as per this link? https://docs.coronalabs.com/api/library/composer/index.html

 

1) If I understand you, the correct code (only the order is changed):

myDownloadedImage = event.target
scene.view:insert(myDownloadedImage)         
myDownloadedImage.alpha = 0
transition.to( myDownloadedImage, { alpha = 1.0 } )

2) Yes, true there are two functions with the same name...I did not check carefully. I could refactor that part of the code.

But, still, the gameLoop function should stay there, even if it does not do anything, but checking the state of the images. Is that what you mean?

 

3) You mean I can use the variables (ie myDownloadedImage1 and 2) outside the API call functions to access the images individually?

 

4) is not a problem. It is simply omitted from the last code above to focus on the core part. You can see my very first post, talking about the scene template. Then, according to you, this means myDownloadedImage:removeSelf() can be omitted.



[TOPIC: post.html]
#17

GO4IT

[GLOBAL: userInfoPane.html]
GO4IT
  • Observer

  • 15 posts
  • Corona SDK

Thank you very much also for @davebolinger for more info.

 

As you seem to have a lot of experience, I would love to refactor the code, but that takes much longer time to implement, as my level of Lua coding is very low. First, I have to examine your code example, to understand what is going on, and I need time to do so. Then, I have to think exactly how it can be applied to my code. That's another challenge.



[TOPIC: post.html]
#18

nick_sherman

[GLOBAL: userInfoPane.html]
nick_sherman
  • Corona Geek

  • 1,820 posts
  • Corona SDK

1) Correct

 

2) I would not keep the gameLoop if you don't need it. 

 

3) Yes. Of course, you'll still need to check they are not nil when you use them.




[topic_controls]
[/topic_controls]

Also tagged with one or more of these keywords: api, remove objects, scene management, two functions, event.target