TI Nspire lua崩溃并引发“ break”错误

问题描述

我正在编写一个脚本,以制作可在TI Nspire CX CAS 2计算器上打开和关闭的小窗口。添加新窗口效果很好,但是尝试删除它们会导致本应删除窗口的功能完成时我的软件崩溃。我也在计算器上尝试过它,但仍然卡住了,但是我能够关闭脚本,然后重新打开它。当我重新打开它时,在第465 a picture of my calculator with the error

行出现了“ break”错误
screen = platform.window
screenx,screeny = screen.width(),screen.height()

-- system stuff

local password = nil
local passverify = nil
local drawObjects = {}
local processes = {}
local rectangle = class()
local backgroundobj = class()
local taskbar = class()
local window = class()
local process = class()
local taskbarHeight = 20
local hasBeenSetup = false
local customSetupProcedures = {}
local isLogedIn = false
local draggingMode = {
move = 0,left = 1,top = 2,right = 3,bottom = 4
}
local readyToDraw = true
local currentDraggingMode = draggingMode.both
local selectedWindowForDragging = nil
local textures = {}
local processstatus = {
waiting = 0,running = 1,halted = 2,dead = 3
}
local specialColors = {
taskbarColor = 0x00969E,windowBackgroundColor = 0xF7F7F7,windowTopBarColor = 0xA2A2A2,windowCloseButtonColor = 0xFF5100,windowMaximizeButtonColor = 0x00FF00,windowMinimizeButtonColor = 0xFFFF00
}

function process:init(run,onKill)
    self.status = processstatus.waiting
    self.co = coroutine.create(run)
    self.onKill = onKill
    coroutine.resume(self.co,self)
end

function process:update()
    if self.status == processstatus.running then
        coroutine.resume(self.co)
    end
end

function process:halt()
    self.status = processstatus.halted
end

function process:resume()
    self.status = processstatus.waiting
end

function process:kill()
    readyToDraw = false
    self.status = processstatus.dead
    self.co = nil
    self.onKill()
    readyToDraw = true
end

function rectangle:init(x,y,width,height)
    self.x = x
    self.y = y
    self.width = width
    self.height = height
end

function rectangle:contains(x,y)
    return self.x<x and x<self.x+self.width and self.y<y and y<self.y+self.height
end

function taskbar:init()

end

function taskbar:draw(gc)
    gc:setColorRGB(specialColors.taskbarColor)
    gc:fillRect(0,screeny-taskbarHeight,screenx,screeny)
    --gc:drawImage(textures[1],screeny-20)
end

function taskbar:isWindow()
    return false
end

function window:init(x,height)
    self.x = x
    self.y = y
    self.draggedOnX = 0
    self.draggedOnY = 0
    self.width = width
    self.height = height
    self.icon = nil
    self.components = {}
    self.focusLevel = 0
    newWindowFocused(#drawObjects+1)
    self.resizable = true
    self:subclassInit(x,height)
end

function window:subclassInit(x,height)

end

function window:getFocusLevel()
    return self.focusLevel
end

function window:draw(gc)
    gc:setColorRGB(specialColors.windowBackgroundColor)
    gc:fillRect(self.x,self.y,self.width,self.height)
    gc:setColorRGB(specialColors.windowTopBarColor)
    gc:fillRect(self.x,10)
    gc:setColorRGB(specialColors.windowMinimizeButtonColor)
    gc:fillRect(self.x+self.width-29,self.y+1,8,8)
    gc:setColorRGB(specialColors.windowMaximizeButtonColor)
    gc:fillRect(self.x+self.width-19,8)
    gc:setColorRGB(specialColors.windowCloseButtonColor)
    gc:fillRect(self.x+self.width-9,8)
    self:drawComponents(gc)
end

function window:drawComponents(gc)
    for i in ipairs(self.components) do
        self.components[i]:draw(gc,self)
    end
end

function window:addComponent(component)
    i = #self.components+1
    self.components[i] = component
    return i
end

function window:decreaseFocus()
    self.focusLevel = self.focusLevel+1
end

function window:isWindow()
    return true
end

function window:contains(x,y)
    return self.x<x and self.y < y and self.width+self.x>x and self.height+self.y>y
end

function window:checkForMouse(x,y)
    if y-self.y <= 3 and self.resizable then
        cursor.set('resize row')
    elseif y-self.y>=self.height-3 and self.resizable then
        cursor.set('resize row')
    elseif x-self.x<=3 and self.resizable then
        cursor.set('resize column')
    elseif x-self.x>=self.width-3 and self.resizable then
        cursor.set('resize column')
    end
end

function window:click(x,n)
    self:focus(n)
    if y-self.y <= 3 and self.resizable then
       selectedWindowForDragging = self
       currentDraggingMode = draggingMode.top
    elseif y-self.y>=self.height-3 and self.resizable then
       selectedWindowForDragging = self
       currentDraggingMode = draggingMode.bottom
    elseif x-self.x<=3 and self.resizable then
       selectedWindowForDragging = self
       currentDraggingMode = draggingMode.left
    elseif x-self.x>=self.width-3 and self.resizable then
        selectedWindowForDragging = self
        currentDraggingMode = draggingMode.right
    elseif y-self.y <= 10 then
        if x>=self.x+self.width-10 then
            self:close()
        elseif x>=self.x+self.width-20 then
            self:maximize()
        elseif x>=self.x+self.width-30 then
            self:minimize()
        else
            selectedWindowForDragging = self
            currentDraggingMode = draggingMode.move
            self.draggedOnX = x - self.x
            self.draggedOnY = y - self.y
        end
    end
    self:clickComponents(x,y)
end

function window:clickComponents(x,y)
    
end

function newWindowFocused(n)
    for window in ipairs(drawObjects) do
        window = drawObjects[window]
        if window:isWindow() then
            if window:getFocusLevel() < n then
                window:decreaseFocus()
            end
        end
    end
end

function window:focus(n)
    self.focusLevel = -1
    newWindowFocused(n)
end

function window:minimize()
    
end

function window:maximize()

end

function window:close()
    processes[1]:kill()
end

function on.resize()
    screenx = screen:width()
    screeny = screen:height()
end

function on.mouseDown(x,y)
    minFocus = 0
    for i = 0,#drawObjects,1 do
        for windown in ipairs(drawObjects) do
            window=drawObjects[windown]
            if window:isWindow() then
                if window:getFocusLevel() == minFocus then
                    if window:contains(x,y) then
                        window:click(x,minFocus)
                        return
                    end
                    minFocus = minFocus+1
                end
            end
        end
    end
end

function on.mouseMove(x,y)
    cursor.set('default')
    if selectedWindowForDragging ~= nil then
        if currentDraggingMode == draggingMode.move then
            selectedWindowForDragging.x = x - selectedWindowForDragging.draggedOnX
            selectedWindowForDragging.y = y - selectedWindowForDragging.draggedOnY
            if selectedWindowForDragging.x+selectedWindowForDragging.draggedOnX<5 then selectedWindowForDragging.x = 5-selectedWindowForDragging.draggedOnX end
            if selectedWindowForDragging.x+selectedWindowForDragging.draggedOnX>screenx-5 then selectedWindowForDragging.x = screenx-selectedWindowForDragging.draggedOnX-5 end
            if selectedWindowForDragging.y<0 then selectedWindowForDragging.y=0 end
            if selectedWindowForDragging.y>screeny-taskbarHeight-5 then selectedWindowForDragging.y = screeny-taskbarHeight-5 end
            elseif currentDraggingMode == draggingMode.top then
                i = selectedWindowForDragging.draggedOnY + y
                selectedWindowForDragging.height = math.max(selectedWindowForDragging.height - selectedWindowForDragging.draggedOnY - y + selectedWindowForDragging.y,20)
                selectedWindowForDragging.y = i
            elseif currentDraggingMode == draggingMode.bottom then
                selectedWindowForDragging.height = math.max(y - selectedWindowForDragging.y,20)
            elseif currentDraggingMode == draggingMode.left then
                i = selectedWindowForDragging.draggedOnX + x
                selectedWindowForDragging.width = math.max(selectedWindowForDragging.width - selectedWindowForDragging.draggedOnX - x + selectedWindowForDragging.x,20)
                selectedWindowForDragging.x = i
            elseif currentDraggingMode == draggingMode.right then
                selectedWindowForDragging.width = math.max(x - selectedWindowForDragging.x,20)
        end
    end
    
        minFocus = 0
        for i = 0,1 do
            for windown in ipairs(drawObjects) do
                window=drawObjects[windown]
                if window:isWindow() then
                    if window:getFocusLevel() == minFocus then
                        if window:contains(x,y) then
                            window:checkForMouse(x,y)
                            return
                        end
                        minFocus = minFocus+1
                    end
                end
            end
        end
end

function on.mouseUp(x,y)
    selectedWindowForDragging = nil
end

local calculator = class(window)

function calculator:subclassInit()
    self.width = 60
    self.height = 90
    self.resizable = false
    self.number = ''
    print(self.number)
    self.button1 = rectangle(5,22,13,10)
    self.button2 = rectangle(24,14,10)
    self.button3 = rectangle(44,10)
    self.button4 = rectangle(5,34,10)
    self.button5 = rectangle(24,10)
    self.button6 = rectangle(44,10)
    self.button7 = rectangle(5,45,10)
    self.button8 = rectangle(24,10)
    self.button9 = rectangle(44,10)
    self.button0 = rectangle(24,57,10)
    self.buttonC = rectangle(5,10)
    self.buttonDot = rectangle(44,10)
    self.buttonPlus = rectangle(6,70,7,7)
    self.buttonMinus = rectangle(15,7)
    self.buttonMultiply = rectangle(40,7)
    self.buttonDivide = rectangle(49,7)
    self.buttonEquals = rectangle(24,69,8)
end

function calculator:drawComponents(gc)
    --gc:drawImage(textures[2],self.x,self.y+10)
    gc:setColorRGB(0x000000)
    gc:setFont('sansserif','r',6)
    if #self.number < 10 then
        gc:drawString(string.sub(self.number,#self.number),self.x+5,self.y+13)
    else
        gc:drawString(string.sub(self.number,#self.number-9,self.y+13)
        print(self.number)
    end
end

function calculator:clickComponents(x,y)
    x = x-self.x
    y = y-self.y-10
    if self.button1:contains(x,y) then self.number = self.number..'1' elseif
    self.button2:contains(x,y) then self.number = self.number..'2' elseif
    self.button3:contains(x,y) then self.number = self.number..'3' elseif
    self.button4:contains(x,y) then self.number = self.number..'4' elseif
    self.button5:contains(x,y) then self.number = self.number..'5' elseif
    self.button6:contains(x,y) then self.number = self.number..'6' elseif
    self.button7:contains(x,y) then self.number = self.number..'7' elseif
    self.button8:contains(x,y) then self.number = self.number..'8' elseif
    self.button9:contains(x,y) then self.number = self.number..'9' elseif
    self.button0:contains(x,y) then self.number = self.number..'0' elseif
    self.buttonPlus:contains(x,y) then self.number = self.number..'+' elseif
    self.buttonMinus:contains(x,y) then self.number = self.number..'-' elseif
    self.buttonMultiply:contains(x,y) then self.number = self.number..'*' elseif
    self.buttonDivide:contains(x,y) then self.number = self.number..'/' elseif
    self.buttonDot:contains(x,y) then self.number = self.number..'.' elseif
    self.buttonEquals:contains(x,y) then
        if not pcall(function()self.number = ''..math.eval(self.number)end) then self.number = 'error' end
    elseif
    self.buttonC:contains(x,y) then self.number = '' end
end


function backgroundobj:draw(gc)
    gc:setColorRGB(0xC4C4C4)
    gc:fillRect(0,screeny-taskbarHeight)
    --gc:drawImage(textures[0],screenx/2-50,(screeny-taskbarHeight)/2-50)
end

function backgroundobj:isWindow()
    return false
end

function loadTextures()
    --textures[0]=image.new(_R.IMG.logo)
    --textures[1]=image.new(_R.IMG.startmenuButton)
    --textures[2]=image.new(_R.IMG.calculator)
end

function on.construction()
    timer.start(1/30)
    checkForSetup()
    checkForLogin()
end

function on.timer()
    screen:invalidate()
end

function encrypt(str,key)
    result = ''
    for i = 1,#str,1 do
        result = result..string.char(math.abs(string.byte(str:sub(i,i))+key+i)%422)
    end
    return result
end
function decrypt(str,1 do
       result = result..string.char(math.abs((string.byte(str:sub(i,i))-key-i)%422))
    end
    return result
end

function checkForLogin()
    
end

function checkForSetup()
    if not hasBeenSetup then
        print('loading textures...')
        loadTextures()
        print('setting up graphics...')
        setupGraphics()
        print('executing default setup procedure...')
        print('setting up password...')
        setupPassword()
        print('executing custom setup procedures...')
        for funct in ipairs(customSetupProcedures) do
            funct()
        end
    end
    hasBeenSetup = true
end

function setupGraphics()
    processes[1] = process(
    function(proc)
        drawObjects[1] = backgroundobj()
    end,function()
       drawObjects[1] = nil
    end)
    drawObjects[2] = taskbar()
end

function setupPassword()
    w1 = calculator(50,100,100)
    drawObjects[3] = w1
    w2 = window(0,50,100)
    drawObjects[4] = w2
end

--system stuff

--graphics stuff

function on.paint(gc)
    if readyToDraw then
    if drawObjects[1]~=nil then
    drawObjects[1]:draw(gc)
    end
    focus = 0
    for i in ipairs(drawObjects) do
        window = drawObjects[i]
        if window:isWindow() then
            focus = math.max(focus,window:getFocusLevel())
        end
    end
    while focus>=0 do
        for i in ipairs(drawObjects) do
            window = drawObjects[i]
            if window:isWindow() and window:getFocusLevel()==focus then
                window:draw(gc)
                focus = focus-1
                break
            end
        end
    end
    drawObjects[2]:draw(gc)
    end
end

--graphics stuff

--default programs

--default programs


编辑:我刚刚发现,如果您正在学生软件中运行脚本,并且该脚本陷入循环中,则按F12会中断循环(就像实际计算器上的“主页”按钮一样。)

解决方法

您的问题是您有机会在on.paint函数中进入无限循环。

您在此处进行检查:

    if drawObjects[1]~=nil then
        drawObjects[1]:draw(gc)
    end

如果此检查为false,则意味着以下for循环将运行0次,因为ipairs希望首先查看索引1或纾困:

    focus = 0
    for i in ipairs(drawObjects) do -- Will run 0 times.
        window = drawObjects[i]
        if window:isWindow() then
            focus = math.max(focus,window:getFocusLevel())
        end
    end

这意味着focus >= 0的while循环条件将无限期运行,因为在循环正文中将发生与ipairs(drawObjects)相同的问题,并且focus永远不会从0改变

    while focus>=0 do
        for i in ipairs(drawObjects) do -- Will run 0 times.
            window = drawObjects[i]
            if window:isWindow() and window:getFocusLevel()==focus then
                window:draw(gc)
                focus = focus-1
                break
            end
        end
    end

您可以通过将focus默认设置为-1来解决此问题,另外,在使用drawObjects[2]之前,您需要检查其是否为零。


或者,将支票更改为

    if drawObjects[1] == nil then
       return -- bail out of function we dont have anything to draw
    end
    drawObjects[1]:draw(gc)