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

[while true do] Slow or Stopping?

Started by EliteGaming, 18 December 2014 - 04:44 AM
EliteGaming #1
Posted 18 December 2014 - 05:44 AM
Hello I'm having a problem with my touchscreen menu. So first obviosly touchscreen programs have to have some sort of getClick()
function to check where the player has clicked the screen at. And the have to do this getClick() constantly with a while true do loop so I have that setup with DireWolf20's button API. So I successfully created a menu with this and a lock for the screen on a seperate computer. So I also have to have another while true do for the interaction of the computers with wireless modems and rednet for a while true do to check if the computer has received a message. So I need the getClick() function and the rednetReceive() function to be in a while true do so I put them both in one. When I launch the program I can interact with the GUI fine but when I click on a further menu also the first selection in the program called "House Control" it freezes basically and I can't interact with the GUI anymore so basically the second menu doesn't have any functionality and whats more weird is that it doesn't give me any errors so I took out the rednetRecieve() in the while true at the bottom and now the whole program worked properly so I'm guessing that the while true is being slow going through the loop and when I click anything it doesn't do anything here is the code sorry that it is kinda long

os.loadAPI("button")
local m = peripheral.wrap("monitor_4")
m.clear()
function newTouchMenu()
m.clear()
m.setBackgroundColor(colors.black)
m.setTextColor(colors.white)
end
  
function fillTable()
   newTouchMenu()
button.clearTable()
	    button.setTable("HouseControl", houseControl, 4,26 ,3,6)
	    button.setTable("N/A", na, 4,26 ,9,12)
	    button.setTable("N/A2", na2, 4,26 ,14,17)
	    button.setTable("Menu", mainMenu, 2,28, 19,20)
    print("Main Menu Filled")
    button.screen()
    end
function houseControl()
button.flash("HouseControl")
newTouchMenu()
button.clearTable()
   button.setTable("Obsidian Collection", oC, 4,26 ,2,6)
	    button.setTable("Energy Status", energyStatus, 4,26 ,8,12)
	    button.setTable("Menu", mainMenu, 2,28, 19,20)
	    button.setTable("N/A", na, 4,26 ,14,17)
print("House Control Filled")
button.screen()
end
function na()
button.flash("N/A")
print("I do nothing; Hurray!")
end
function na2()
button.flash("N/A2")
print("I do nothing 2; Hurray!")
end
function oC()
button.toggleButton("Obsidian Collection")
rednet.send(63, "oc")
end
function mainMenu()
button.flash("Menu")
m.clear()
   fillTable()
--   refresh()
end

function energyStatus()
button.flash("Energy Status")
m.clear()
sleep(.1)
m.setCursorPos(6,10)
m.setTextScale(1)
m.setTextColor(colors.white)
m.write("Loading Information")
sleep(.5)
m.clear()
m.write("Loading Information.")
sleep(.5)
m.clear()
m.write("Loading Information..")
sleep(.5)
m.clear()
m.write("Loading Information...")
sleep(.5)
m.clear()
button.clearTable()
button.setTable("Refresh", refresh, 2,28,0,2)
button.setTable("Menu", mainMenu, 2,28, 19,20)
button.screen()
m.setTextScale(1.4)
m.setCursorPos(0,10)
button.centerText("Power : ..en")
m.setCursorPos(21,3)
end

function getPower()
c = peripheral.wrap("right")
while true do
en = c.getEnergyStored("south")
sleep(.1)
end
end

function lockScreen()
m.clear()
button.clearTable()
m.setTextColor(colors.red)
m.setCursorPos(3,9)
button.centerText("Locked")
m.setCursorPos(6,10)
m.write("Unlock with Digitizer")
m.setCursorPos(3,11)
m.write("Given by Owner EliteGaming")
end

function getClick()
   local event,side,x,y = os.pullEvent()
   if event=="monitor_touch" then
	 button.checkxy(x,y)
   end
end
function rednetReceiver()
senderID, message = rednet.receive()
if senderID == 62 then
   if message == "unlock" then
	 fillTable()
   end
    if message == "lock" then
	 lockScreen()
    end
    if message == "oc" then
rednet.send(63, "oc")
	  end
  end
end
function refresh()
button.flash("Refresh")
   button.screen()
end
fillTable()
-- refresh()
rednet.open("front")
while true do
getClick()
rednetReceiver()
end

And this is the D20 button API [Custom]:

local mon = peripheral.wrap("monitor_0")
mon.setTextScale(1)
mon.setTextColor(colors.white)
local button={}
mon.setBackgroundColor(colors.black)
	
function clearTable()
button={}
end

function setTable(name, func, xmin, xmax, ymin, ymax)
   button[name] = {}
   button[name]["func"] = func
   button[name]["active"] = false
   button[name]["xmin"] = xmin
   button[name]["ymin"] = ymin
   button[name]["xmax"] = xmax
   button[name]["ymax"] = ymax
end
function funcName()
   print("You clicked buttonText")
end
	   
function fillTable()
   setTable("ButtonText", funcName, 5, 25, 4, 8)
end	
function fill(text, color, bData)
   mon.setBackgroundColor(color)
   local yspot = math.floor((bData["ymin"] + bData["ymax"]) /2)
   local xspot = math.floor((bData["xmax"] - bData["xmin"] - string.len(text)) /2) +1
   for j = bData["ymin"], bData["ymax"] do
	  mon.setCursorPos(bData["xmin"], j)
	  if j == yspot then
		 for k = 0, bData["xmax"] - bData["xmin"] - string.len(text) +1 do
		    if k == xspot then
			   mon.write(text)
		    else
			   mon.write(" ")
		    end
		 end
	  else
		 for i = bData["xmin"], bData["xmax"] do
		    mon.write(" ")
		 end
	  end
   end
   mon.setBackgroundColor(colors.black)
end
	
function screen()
   local currColor
   for name,data in pairs(button) do
	  local on = data["active"]
	  if on == true then currColor = colors.lime else currColor = colors.red end
	  fill(name, currColor, data)
   end
end
function toggleButton(name)
   button[name]["active"] = not button[name]["active"]
   screen()
end	
function flash(name)
   toggleButton(name)
   screen()
   sleep(0.15)
   toggleButton(name)
   screen()
end
											
function checkxy(x, y)
   for name, data in pairs(button) do
	  if y>=data["ymin"] and  y <= data["ymax"] then
		 if x>=data["xmin"] and x<= data["xmax"] then
		    data["func"]()
		    return true
		    --data["active"] = not data["active"]
		    --print(name)
		 end
	  end
   end
   return false
end
	
function heading(text)
   w, h = mon.getSize()
   mon.setCursorPos((w-string.len(text))/2+1, 1)
   mon.write(text)
end
	
function label(w, h, text)
   mon.setCursorPos(w, h)
   mon.write(text)
end
function centerText(text)
x, y = mon.getSize()
b, m = mon.getCursorPos()
mon.setCursorPos((x-string.len(text))/2+b, m)
mon.write(text)
end
Thanks for the help!
Bomb Bloke #2
Posted 18 December 2014 - 06:04 AM
rednet.receive() sits there waiting for a rednet message to appear in the event queue. If that never happens, it sits and waits forever (unless you specified a time out value for it - eg rednet.receive(5) waits five seconds, and returns nil if no message is found in that time).

Speaking of which, that getClick() function you're using pulls whatever event is sitting at the front of the event queue, and discards it unless it's a "monitor_touch" event (in which case it passes it off to button.checkxy() for further inspection). Guess what happens if a rednet message is sitting at the front of the event queue when your script decides to check for a button press? It throws it out. Further complicating things, certain other functions can pull events too - for example, sleep() tosses out all events in the queue until it finds a certain timer event. I call these sorts of functions "event eaters".

When you've got a lot of "event eating" functions around and can't easily replace them with your own versions (ones which make sure to hang on to all important events and deal with them properly), the parallel API comes in handy. Have a look at this post for an example of how it might be used.
EliteGaming #3
Posted 18 December 2014 - 02:58 PM
Ok thank you, I didn't have any idea of that stuff since I'm fairly new to Lua and ComputerCraft but I try my best. So how would I make this more cleaner?
Edited on 18 December 2014 - 02:00 PM
EliteGaming #4
Posted 19 December 2014 - 06:48 AM
Hello?
Bomb Bloke #5
Posted 19 December 2014 - 08:19 AM
Reading through your code takes time, y'know. :P/>/>

It looks like if you re-write these two functions along these lines:

local function getClick()
	while true do
		local _,_,x,y = os.pullEvent("monitor_touch")
		button.checkxy(x,y)
	end
end

local function rednetReceiver()
	while true do
		local senderID, message = rednet.receive()
		if senderID == 62 then
			if message == "unlock" then
				fillTable()

			elseif message == "lock" then
				lockScreen()

			elseif message == "oc" then
				rednet.send(63, "oc")
			end
		end
	end
end

… then you should be able to re-write this bit at the bottom:

while true do
	getClick()
	rednetReceiver()
end

… as just:

parallel.waitForAny(getClick, rednetReceiver)
Edited on 19 December 2014 - 07:21 AM
Cycomantis #6
Posted 19 December 2014 - 11:00 PM
Personally I would remove both function and just make the while true loop something like the following. That way you are only utilizing 1 os.pullEvent().


while true do
local e,p1,p2,p3 = os.pullEvent()
if e == "monitor_touch" then
    button.checkxy(p2,p3)
elseif e == "rednet_message" then
    if p1 == 62 then
        if p2 == "unlock" then
            fillTable()
        elseif p2 == "lock" then
            lockScreen()
        elseif p2 == "oc" then
            rednet.send(63, "oc")
        end
    end
end
end
EliteGaming #7
Posted 20 December 2014 - 12:12 AM
OK cool Ill test both and see which works better. Thanks for all your help guys really appreciate it
Edited on 19 December 2014 - 11:13 PM