This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
skatelow's profile picture

[LUA] [QUESTION] How to run a loop but exit when key is pressed?

Started by skatelow, 26 November 2012 - 06:35 AM
skatelow #1
Posted 26 November 2012 - 07:35 AM
Hello everyone,

I've been trying to make a simple treefarm program and for that i want a computer to run a program that lets the treefarm run but stops the program when i press enter.
So it's just quitting a loop on a key press. I've already tried with parallels, but i don't seem to be able to get it right. Maybe because i use os.pullEventRaw in one of the loops.
Anyways, I woud find it very helpful if someone could help me, as it is something i would like to use more often in my programs.
thanks! :D/>/>
Kingdaro #2
Posted 26 November 2012 - 07:37 AM
You exit out of a loop using break.

while true do
  local event, param = os.pullEvent()
  if event == 'key' then
    if param == keys.enter then
      break
    else
      print "You didn't press enter!"
    end
  end
end
Breaks out of the loop if enter is pressed, tells you otherwise if you pressed something else.
skatelow #3
Posted 28 November 2012 - 05:52 AM
but i want to run a program at the same time, not wait for something to be pressed.
dissy #4
Posted 28 November 2012 - 06:03 AM
Hello everyone,

I've been trying to make a simple treefarm program and for that i want a computer to run a program that lets the treefarm run but stops the program when i press enter.
So it's just quitting a loop on a key press. I've already tried with parallels, but i don't seem to be able to get it right. Maybe because i use os.pullEventRaw in one of the loops.
Anyways, I woud find it very helpful if someone could help me, as it is something i would like to use more often in my programs.
thanks! :)/>/>

You'll probably want to take a look at the parallel API, specifically the command parallel.waitForAny

This command lets you give it multiple functions that (more or less) run at the same time. waitForAny will exit when at least one of those functions returns/exits.

One function should contain a "while true do" loop that never exits, and contain your main code.
The second function will watch for key events.

If you don't care which key is pressed, that second function can just poll for a key event, and it will return once something is pressed.
Otherwise it can be a while loop watching key events, which only does a break if the correct key is pressed, causing it to exit.

I can post verified code later today once I get home if desired.
Lyqyd #5
Posted 28 November 2012 - 06:14 AM
I really wish people wouldn't leap immediately to parallel when all the problem needs is basic event handling.


while true do
  --other code
  os.queueEvent("event_marker")
  local breakOut = false
  while true do
    e = os.pullEvent()
    if e == "key" then --add any other events that should break the loop if necessary
      breakOut = true
    elseif e == "event_marker" then
      break
    end
  end
  if breakOut then break end
end

What's going on here is that we queue an event manually, then check through the event queue to see if any of our break-trigger events (like a key press) have come through. If not, our custom event will be waiting in the queue and we will continue onward.
billysback #6
Posted 28 November 2012 - 07:53 AM
or

local on = true
local interval = 0.1

local timer = os.startTimer(0)
while on do
   local event, p1, p2, p3 = os.pullEvent()
   if event == "timer" then
	   --Update program
	   timer = os.startTimer(interval)
   else
	   local key = p1
	   if key == 29 then --left control
		   on = false
	   end
   end
end
Advantages over other alternatives:
"on" can be reset outside of the loop by functions without the use of returns, freeing up your return values for functions you use in the loop
updates on a timer instead of every tick, the interval value means you can reset this.

I think the most effective way of running the program using a while loop is to start the program up before the while loop then update it only when required;
such as when a new button is pressed etc.
dissy #7
Posted 28 November 2012 - 12:06 PM
I really wish people wouldn't leap immediately to parallel when all the problem needs is basic event handling.

Sorry then.
Kingdaro #8
Posted 28 November 2012 - 12:15 PM
I really wish people wouldn't leap immediately to parallel when all the problem needs is basic event handling.
It's really a matter of preference though. I personally like to have my code separated into functions, with one function serving as a "press this key to exit" and the other(s) being my main program code.


function program()
  while true do
    -- stuff
    sleep(0.5)
  end
end

function exitProgram()
  repeat
    local ev, key = os.pullEvent('key')
  until key == keys.backspace -- or whatever key i decide to use
end

parallel.waitForAny(program, exitProgram)

To me, this makes more sense, but again, preferences.
Lyqyd #9
Posted 28 November 2012 - 01:55 PM
It's more the use of unnecessary coroutines to handle things that can easily be taken care of in line with the rest of the code that bugs me. There are places where coroutines are necessary, and even the rare occasion when the parallel API is a useful way to manage them. For the most part though, the grand majority of the time I see use of the parallel API suggested it's as an "easy way out" of proper event handling, which is a little disappointing.
Kingdaro #10
Posted 29 November 2012 - 12:52 AM
It's more the use of unnecessary coroutines to handle things that can easily be taken care of in line with the rest of the code that bugs me. There are places where coroutines are necessary, and even the rare occasion when the parallel API is a useful way to manage them. For the most part though, the grand majority of the time I see use of the parallel API suggested it's as an "easy way out" of proper event handling, which is a little disappointing.
It's there for a reason, and if it makes things easier, why not use it? :D/>
skatelow #11
Posted 29 November 2012 - 03:33 AM
the code i have now is this:

local function treefarm()

rednet.open("right")
term.clear()
term.setCursorPos(1,1)
print("-------------------------------------------------")
print("	  [Skatelow Industries]  Treefarm")
print("-------------------------------------------------")
print("")
print("		   Farming in process...")
print("		   Press [Enter] to stop")

--Deployers bevoorraden (toggle latch)
rs.setBundledOutput("bottom",colors.blue)

while true do
--Plant sapling
rs.setBundledOutput("bottom",colors.white)
sleep(0.5)
rs.setBundledOutput("bottom",0)
sleep(0.5)
--Bonemeal
rs.setBundledOutput("bottom",colors.brown)
sleep(0.5)
rs.setBundledOutput("bottom",0)
sleep(1)
--Harvest Tree
rednet.send(3,"HarVEST")
sleep(13)
end

end


local function stop()
e, a = os.pullEventRaw()
if e == "key" and a == "28" then
rs.setBundledOutput("bottom",colors.blue)
return
end
end

parallel.waitForAny(treefarm, stop)

But the problem is that it only loops once and then stops. I guess it's because the treefarm function returns after one loop or something, i'm not really sure.
Kingdaro #12
Posted 29 November 2012 - 03:38 AM
There are some problems with your stop() function. First that you should probably be using pullEvent() instead of pullEventRaw(), and that the 28 shouldn't have quotes around it. You should probably also loop it, so that it keeps checking for an event instead of only once.


local function stop()
while true do
e, a = os.pullEvent()
if e == "key" and a == 28 then
rs.setBundledOutput("bottom",colors.blue)
return
end
end
end