Jump to content
  • Sky
  • Blueberry
  • Slate
  • Blackcurrant
  • Watermelon
  • Strawberry
  • Orange
  • Banana
  • Apple
  • Emerald
  • Chocolate
  • Charcoal
  • 0
gibbo3771

Event function losing scope?

Question

So I am pretty new to Lua, having only used it very lightly in the past for basic scripts. I am having some issue that hopefully someone can help me with and it can be best described as a "function losing scope". I'll post the code and explain it.

local buildWitherButton = app:addChild(GUI.roundedButton(buttonLeftAnchor, 13, buttonWidth, 7, buttonDefaultColor, bgColor, buttonPressedColor, bgColor, "BUILD WITHER"))
buildWitherButton.onTouch = function()
  if(powerButton.pressed ~= true)
  then
    throwWitherBuildError("Can't build Wither with shield down, are you mad?!?!?")
    return
  end
  -- Do wither stuff
  note.play(80)
  clearAndInitProgressBar()
  e = event.timer(1, progressUpdate, 10) -- MC states 10 seconds to build a Wither, so this rough timer will work for now. Call the function every 1 second, for 10 seconds
end


buildProgressBar = app:addChild(GUI.progressBar(buttonLeftAnchor, 20, buttonWidth, buttonPressedColor, fgColor, textDefaultColor, 0, true, true, "Building Wither : ", "%"))
buildProgressBar.hidden = true

function clearAndInitProgressBar()
  buildProgressBar.hidden = false
  buildProgressBar.value = 0
end

local function progressUpdate()
  buildProgressBar.value = buildProgessBar.value + 50 -- This lines does literally nothing, I can type it wrong and everything, or even assign strings, booleans, tables. It's like the line is completely ignored
  note.play(75) -- This beeps. wtf is happening above?
end

 

I am using a GUI library, which can be found HERE. I completely lack the knowledge of the language and API to understand what is wrong, it's also impossible to Google :p.

Any ideas?

Link to post
Share on other sites

6 answers to this question

Recommended Posts

  • 0

btw, if the onTouch callback is made to an event.listern (inside the gui library you are using), which it likely is, then you would see the crash log in /tmp/event.log (it would be crashing because the assert of the 2nd argument is a function and NOT nil)

Link to post
Share on other sites
  • 0
11 hours ago, payonel said:

Looks like your you've declared `progressUpdate` after your used it, thus passing nil to event.timer for its 2nd argument. Which, btw, isn't allowed, it should error (it requires you pass a function there)

So how come  'clearAndInitProgressBar' is working just fine? I thought it was due to declaring it as not local but changing that seems to have no effect. 

10 hours ago, payonel said:

btw, if the onTouch callback is made to an event.listern (inside the gui library you are using), which it likely is, then you would see the crash log in /tmp/event.log (it would be crashing because the assert of the 2nd argument is a function and NOT nil)

This piece of code is not crashing, but it should? That's weird.

 

EDIT: Would also like to start that this:

throwWitherBuildError("Can't build Wither with shield down, are you mad?!?!?")

Is also declared at the end of my file, so technically this should work either right?

Link to post
Share on other sites
  • 0
5 hours ago, gibbo3771 said:

So how come  'clearAndInitProgressBar' is working just fine? I thought it was due to declaring it as not local but changing that seems to have no effect.

It might be that you've re-run this program again after the edits with the global declaration still existing. Hard to say without the whole program.

5 hours ago, gibbo3771 said:

This piece of code is not crashing, but it should? That's weird.

It's not that your program will crash but that the listener, which technically exists beyond the scope of your program, will crash and report the error in /tmp/event.log

 

You should take a hard look at how Lua scope works and ponder why it works the way it does. Particularly that scope is built line by line at 'run' time.

local buildWitherButton = app:addChild(GUI.roundedButton(buttonLeftAnchor, 13, buttonWidth, 7, buttonDefaultColor, bgColor, buttonPressedColor, bgColor, "BUILD WITHER"))

local buildProgressBar -- # forward declaration whilst still local to program. Now the definitions below will have the ref at run time.

local function clearAndInitProgressBar()
  buildProgressBar.hidden = false
  buildProgressBar.value = 0
end

local function progressUpdate()
  buildProgressBar.value = buildProgessBar.value + 50
  note.play(75)
end

buildWitherButton.onTouch = function()
  if(powerButton.pressed ~= true)
  then
    throwWitherBuildError("Can't build Wither with shield down, are you mad?!?!?")
    return
  end
  -- # Do wither stuff
  note.play(80)
  clearAndInitProgressBar()
  e = event.timer(1, progressUpdate, 10)
end

--# buildProgressBar is now being defined as declared above and will still be local
buildProgressBar = app:addChild(GUI.progressBar(buttonLeftAnchor, 20, buttonWidth, buttonPressedColor, fgColor, textDefaultColor, 0, true, true, "Building Wither : ", "%"))
buildProgressBar.hidden = true

 

Link to post
Share on other sites
  • 0

Ah right, the scope makes sense given that it's a interpreted line by line language. I had a look at the log and right enough, buildProgressBar is nil. So I tried changing the code around, to what you have described so the variable is declared and known, but unassigned. Kinda safely assuming functions are passed as reference, rather than a copy as it would make less sense. I can link the whole program, can't see it helping much!

local c = require("component")
local GUI = require("GUI")
local buffer = require("doubleBuffering")
local image = require("image")
local note = require("note")
local gpu = c.gpu
local rs = c.redstone
local sides = require("sides")
local event = require("event")

-- App parameters
local bgColor = 0x03162A
local fgColor = 0x22065B
local buttonDefaultColor = 0xC22828
local buttonPressedColor = 0xA4Ef7F
local textDefaultColor = 0xEEF9FF
local shieldPowered = false

-- App instance
local app = GUI.application()

-- Background, covers entire screen
app:addChild(GUI.panel(1, 1, app.width, app.height, bgColor))

-- Title bannner
app:addChild(GUI.panel(1, 1, app.width, 3, fgColor))
local titleText = app:addChild(GUI.label(1, 2, app.width, app.height, textDefaultColor, "Wither Farm Control System"))
titleText:setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)

-- Left widget alignment and sizing
local buttonWidth = 60
local buttonLeftAnchor = 3

-- The shield button, this toggles the shield on and off
local text = "SHIELD POWER"
local powerButton = app:addChild(GUI.roundedButton(buttonLeftAnchor, 5, buttonWidth, 7, buttonDefaultColor, textDefaultColor, buttonPressedColor, textDefaultColor,  text))
powerButton.switchMode = true
powerButton.onTouch = function()
  note.play(80)
  shieldPowered = not shieldPowered
  if(shieldPowered == true)
  then
    rs.setOutput(sides.left, 0) -- TODO set to 15 when ready
  else
    rs.setOutput(sides.left, 0)
  end
end

-- This button will eventually be used to build a wither
local buildWitherButton = app:addChild(GUI.roundedButton(buttonLeftAnchor, 13, buttonWidth, 7, buttonDefaultColor, bgColor, buttonPressedColor, bgColor, "BUILD WITHER"))
-- Displays build status in %
local buildProgressBar

local function clearAndInitProgressBar()
  buildProgressBar.value = 0
  buildProgressBar.hidden = false
end

local function progressUpdate()
  buildProgressBar.value = buildProgressBar.value + 10
  note.play(75)
end

buildWitherButton.onTouch = function()
  if(powerButton.pressed ~= true)
  then
    throwWitherBuildError("Can't build Wither with shield down, are you mad?!?!?")
  else
    clearAndInitProgressBar()
    event.timer(1, progressUpdate, 10)
  end
end

buildProgressBar = app:addChild(GUI.progressBar(buttonLeftAnchor, 22, buttonWidth, buttonPressedColor, fgColor, textDefaultColor, 0, true, true, "Building Wither : ", "%"))
buildProgressBar.hidden = true

-- Throws an error if wither can't be built, with a reason why
local function throwWitherBuildError(text)
  note.play(30)
  return GUI.alert(text)
end

app:draw(true)
app:start()

 

Link to post
Share on other sites
  • 0

I'll try to explain what is happening in the perspective of the lua compiler

In this code chunk, we are assigning buildWitherButton.onTouch to a function.

powerButton is declared in the local scope previously, and is captured (linked) from the current scope

throwWitherBuildError is not declared, and thus no variable is linked. When executing this code, access to this variable will be checked in the global scope, _G. If when this code is executed there is no _G.throwWitherBuildError then it will resolve as nil.

clearAndInitProgressBar is declared in the local scope previously, and is captured (linked) from the current scope

event is declared in the local scope previously, and is captured (linked) from the current scope

buildWitherButton.onTouch = function()
  if(powerButton.pressed ~= true)
  then
    throwWitherBuildError("Can't build Wither with shield down, are you mad?!?!?")
  else
    clearAndInitProgressBar()
    event.timer(1, progressUpdate, 10)
  end
end

Later we see this:

local function throwWitherBuildError(text)
  note.play(30)
  return GUI.alert(text)
end

which creates a local function throwWitherBuildError, and it is never used nor linked

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use and Privacy Policy.