Lua (CC) GUI class 将所有组件绘制在同一个 window 中,当被告知要在单独的 windows 中绘制它们时
Lua (CC) GUI class draws all components in the same window when told to draw them in separate windows
序曲
ComputerCraft 是 Minecraft (Forge) 的 mod,它在游戏中添加了一个基于 lua 的原始计算机。使用这台计算机,可以编写程序以各种方式与 Minecraft 世界交互。 ComputerCraft 问题是否适用于 Whosebug 之前已经在其他问题中进行过辩论,但我认为它 是 适用的,因为 mod 在很大程度上是关于编程的,虽然进行了一些 ComputerCraft 专有 API 调用,但这个问题中没有任何概念不适用于其他与 ComputerCraft 无关的 lua 程序(当然除非问题是由错误引起的在 ComputerCraft 本身)。使用的 API 的文档可以在 http://www.computercraft.info/wiki/Category:APIs 找到。
注意:如果您没有 ComputerCraft 经验,请不要惊慌;我相信这个问题可能与 ComputerCraft 完全无关,而是由我未能掌握的 lua 中 OOP 的一些复杂性引起的。在我认为有必要解释我正在进行的专有调用的最重要方面的地方,我对代码进行了注释。有什么不明白的可以评论,我会解释的。
如果您希望能够在没有 Minecraft 的情况下 运行 代码示例,可以使用名为 CCEmuRedux 的优秀 ComputerCraft 模拟器。我已经在实际的 ComputerCraft 和 CCEmuRedux 上测试了我的代码,结果相同,尽管 CCEmuRedux 似乎不支持监视器。需要 "Advanced" 电脑才能看到颜色。
问题
在 ComputerCraft 1.75(和 CCEmuRedux @ ComputerCraft 1.79)中,给出以下 class gui,以及一个尝试在每个中绘制基本按钮的测试程序两个不同的 windows 使用 gui class,两个按钮都在第二个 window 中绘制。从图形上看,guiTest.lua 的结果是 https://i.imgur.com/llFDlYI.png,而我希望在 Window 中绘制第一个(橙色)按钮。虽然我有一些关于它为什么会这样的理论,我没有必要的 lua 经验来弄清楚如何修复它。这是一个 MWE。
代码示例
gui.lua
--Meta class
gui = {t, vpx, vpy}
function gui:new(t, title) -- I'm aware this constructor is not in keeping with the referenced Tutorialspoint article, it is of no consequence in this example
local o = o or {}
setmetatable(o, self)
self.__index = self
self.t = t
local sX, sY = self.t.getSize() -- get the size of the virtual terminal and save it to vpx, vpy
self.vpx = sX
self.vpy = sY
self.t.setCursorPos(1, 1) -- put cursor at the start of the virtual terminal
self.t.write(tostring(title)) -- note that this WORKS, it prints one title per Window as seen in the screenshot
return o
end
function gui:drawButton(x, y, sX, sY, colour)
self.t.setCursorPos(x, y) -- set the cursor to the button's first x- and y-coords
self.t.setTextColor(colours.black) -- set text colour to black
self.t.setBackgroundColor(colour) -- set background colour to the colour of the button
for iY = 1, sY do
for iX = 1, sX do
self.t.write("#") -- print hashtags to represent the button until we reach sX and sY
end
self.t.setCursorPos(x, y + iY) -- move cursor a line down, and back to button's first x-coord
end
self.t.setCursorPos(self.vpx, self.vpy) -- get cursor out of the way so the screenshot will be prettier
end
guiTest.lua
dofile('gui.lua')
local w1 = window.create(term.current(), 2, 2, 22, 15)
local w2 = window.create(term.current(), 26, 2, 22, 15) -- creates virtual windows in a terminal, acting as terminals of their own
-- window.create() arguments: terminal object to create window on, x position, y position, x size, y size
local g1 = gui:new(w1, "Window 1") -- create gui object for the first window
local g2 = gui:new(w2, "Window 2") -- create gui object for the second window
g1:drawButton(5, 3, 3, 2, colours.orange) -- should draw in w1, draws in w2
g2:drawButton(10, 8, 4, 4, colours.green) -- should draw in w2, draws in w2
尝试过的解决方案
为了它的价值,我一直在遵循 Lua OOP 秘诀 @ https://www.tutorialspoint.com/lua/lua_object_oriented.htm。这是我的第二个基于 lua 的程序,所以我希望它是一个 "easy" 问题。不过,我对 OOP 在其他几种语言(特别是 Java)中的工作原理有不止一个基本的了解,因此我的程序员的 "Spidey-Sense" 告诉我一些变量,例如 t,不是 "local enough"(同一个变量被两个 windows 使用),或者 gui 对象中的某个引用得到当一个新的 gui 对象被创建时被覆盖。
因此,我尝试将 table gui 设为本地,以确保它不会被覆盖:
local gui = {t, vpx, vpy}
...但它在 "gui.lua" (setmetatable(o, self)
) 的第 6 行出现错误 attempt to index ?
,所以我尝试了(意识到我将无法访问该函数来自外部 gui.lua,因为它是本地的):
local function gui:drawButton(x, y, sX, sY, colour)
... 结果是 guiTest.lua:1: bios.lua:14 [string "gui.lua"]:17:'(' expected
。第 17 行是上面代码标签中 gui:drawButton()
的定义。在我有限的 ComputerCraft 经验中,这种格式错误的错误消息通常意味着 lua 解释器或 CraftOS 异常混乱™,但我认为它的要点是 "you can't make an object method local",因为我 can 使 other 以与我在此处尝试的方式类似的方式在本地运行。
一般来说,window.create()
或使用 window API 都不是问题,因为使用单独的监视器而不是单独的 windows 在同一台显示器上。本质上:
dofile('gui.lua')
local w = window.create(term.current(), 2, 2, 22, 15)
local m = peripheral.wrap('top') -- m becomes the Monitor physically on top of the ComputerCraft Computer
local gw = gui:new(w, "Window") -- create gui object for the Window
-- m is a terminal object, just like w, so we can still do
local gm = gui:new(m, "Monitor") -- create gui object for the Monitor
gw:drawButton(5, 3, 3, 2, colours.orange) -- should draw in w, draws in m
gm:drawButton(10, 8, 4, 6, colours.green) -- should draw in m, draws in m
也许有一种方法可以将函数存储为局部变量,类似于
local gui:printFoo = function() print("foo") end
self:printFoo() -- prints "foo"...?
...或者更有可能的是,这个问题是我完全错过的。
结论
为了简化一个长问题,定义两个 gui 对象,每个对象对应两个虚拟控制台 windows,并尝试在每个对象上绘制一个按钮虚拟控制台 windows 使用它们各自的 gui 对象,导致两个按钮被绘制在同一个虚拟控制台 window 上。为什么?
是的,Lua 中的 OOP 对于 Lua 初学者来说很难,尽管他们非常了解 OOP 语言(例如 Java)。
--Meta class
gui = {} -- class is a global variable, no default properties exist
function gui:new(t, title) -- t = window, self = your class "gui"
local o = {} -- creating NEW object
setmetatable(o, self) -- link the object with the class
self.__index = self
o.t = t -- save window into object (not into class)
local sX, sY = t.getSize() -- get the size of the virtual terminal
o.vpx = sX -- save window's properties into object (not into class)
o.vpy = sY
t.setCursorPos(1, 1)
t.write(tostring(title))
return o
end
function gui:drawButton(x, y, sX, sY, colour) -- self = object
....
end
序曲
ComputerCraft 是 Minecraft (Forge) 的 mod,它在游戏中添加了一个基于 lua 的原始计算机。使用这台计算机,可以编写程序以各种方式与 Minecraft 世界交互。 ComputerCraft 问题是否适用于 Whosebug 之前已经在其他问题中进行过辩论,但我认为它 是 适用的,因为 mod 在很大程度上是关于编程的,虽然进行了一些 ComputerCraft 专有 API 调用,但这个问题中没有任何概念不适用于其他与 ComputerCraft 无关的 lua 程序(当然除非问题是由错误引起的在 ComputerCraft 本身)。使用的 API 的文档可以在 http://www.computercraft.info/wiki/Category:APIs 找到。
注意:如果您没有 ComputerCraft 经验,请不要惊慌;我相信这个问题可能与 ComputerCraft 完全无关,而是由我未能掌握的 lua 中 OOP 的一些复杂性引起的。在我认为有必要解释我正在进行的专有调用的最重要方面的地方,我对代码进行了注释。有什么不明白的可以评论,我会解释的。
如果您希望能够在没有 Minecraft 的情况下 运行 代码示例,可以使用名为 CCEmuRedux 的优秀 ComputerCraft 模拟器。我已经在实际的 ComputerCraft 和 CCEmuRedux 上测试了我的代码,结果相同,尽管 CCEmuRedux 似乎不支持监视器。需要 "Advanced" 电脑才能看到颜色。
问题
在 ComputerCraft 1.75(和 CCEmuRedux @ ComputerCraft 1.79)中,给出以下 class gui,以及一个尝试在每个中绘制基本按钮的测试程序两个不同的 windows 使用 gui class,两个按钮都在第二个 window 中绘制。从图形上看,guiTest.lua 的结果是 https://i.imgur.com/llFDlYI.png,而我希望在 Window 中绘制第一个(橙色)按钮。虽然我有一些关于它为什么会这样的理论,我没有必要的 lua 经验来弄清楚如何修复它。这是一个 MWE。
代码示例
gui.lua
--Meta class
gui = {t, vpx, vpy}
function gui:new(t, title) -- I'm aware this constructor is not in keeping with the referenced Tutorialspoint article, it is of no consequence in this example
local o = o or {}
setmetatable(o, self)
self.__index = self
self.t = t
local sX, sY = self.t.getSize() -- get the size of the virtual terminal and save it to vpx, vpy
self.vpx = sX
self.vpy = sY
self.t.setCursorPos(1, 1) -- put cursor at the start of the virtual terminal
self.t.write(tostring(title)) -- note that this WORKS, it prints one title per Window as seen in the screenshot
return o
end
function gui:drawButton(x, y, sX, sY, colour)
self.t.setCursorPos(x, y) -- set the cursor to the button's first x- and y-coords
self.t.setTextColor(colours.black) -- set text colour to black
self.t.setBackgroundColor(colour) -- set background colour to the colour of the button
for iY = 1, sY do
for iX = 1, sX do
self.t.write("#") -- print hashtags to represent the button until we reach sX and sY
end
self.t.setCursorPos(x, y + iY) -- move cursor a line down, and back to button's first x-coord
end
self.t.setCursorPos(self.vpx, self.vpy) -- get cursor out of the way so the screenshot will be prettier
end
guiTest.lua
dofile('gui.lua')
local w1 = window.create(term.current(), 2, 2, 22, 15)
local w2 = window.create(term.current(), 26, 2, 22, 15) -- creates virtual windows in a terminal, acting as terminals of their own
-- window.create() arguments: terminal object to create window on, x position, y position, x size, y size
local g1 = gui:new(w1, "Window 1") -- create gui object for the first window
local g2 = gui:new(w2, "Window 2") -- create gui object for the second window
g1:drawButton(5, 3, 3, 2, colours.orange) -- should draw in w1, draws in w2
g2:drawButton(10, 8, 4, 4, colours.green) -- should draw in w2, draws in w2
尝试过的解决方案
为了它的价值,我一直在遵循 Lua OOP 秘诀 @ https://www.tutorialspoint.com/lua/lua_object_oriented.htm。这是我的第二个基于 lua 的程序,所以我希望它是一个 "easy" 问题。不过,我对 OOP 在其他几种语言(特别是 Java)中的工作原理有不止一个基本的了解,因此我的程序员的 "Spidey-Sense" 告诉我一些变量,例如 t,不是 "local enough"(同一个变量被两个 windows 使用),或者 gui 对象中的某个引用得到当一个新的 gui 对象被创建时被覆盖。
因此,我尝试将 table gui 设为本地,以确保它不会被覆盖:
local gui = {t, vpx, vpy}
...但它在 "gui.lua" (setmetatable(o, self)
) 的第 6 行出现错误 attempt to index ?
,所以我尝试了(意识到我将无法访问该函数来自外部 gui.lua,因为它是本地的):
local function gui:drawButton(x, y, sX, sY, colour)
... 结果是 guiTest.lua:1: bios.lua:14 [string "gui.lua"]:17:'(' expected
。第 17 行是上面代码标签中 gui:drawButton()
的定义。在我有限的 ComputerCraft 经验中,这种格式错误的错误消息通常意味着 lua 解释器或 CraftOS 异常混乱™,但我认为它的要点是 "you can't make an object method local",因为我 can 使 other 以与我在此处尝试的方式类似的方式在本地运行。
一般来说,window.create()
或使用 window API 都不是问题,因为使用单独的监视器而不是单独的 windows 在同一台显示器上。本质上:
dofile('gui.lua')
local w = window.create(term.current(), 2, 2, 22, 15)
local m = peripheral.wrap('top') -- m becomes the Monitor physically on top of the ComputerCraft Computer
local gw = gui:new(w, "Window") -- create gui object for the Window
-- m is a terminal object, just like w, so we can still do
local gm = gui:new(m, "Monitor") -- create gui object for the Monitor
gw:drawButton(5, 3, 3, 2, colours.orange) -- should draw in w, draws in m
gm:drawButton(10, 8, 4, 6, colours.green) -- should draw in m, draws in m
也许有一种方法可以将函数存储为局部变量,类似于
local gui:printFoo = function() print("foo") end
self:printFoo() -- prints "foo"...?
...或者更有可能的是,这个问题是我完全错过的。
结论
为了简化一个长问题,定义两个 gui 对象,每个对象对应两个虚拟控制台 windows,并尝试在每个对象上绘制一个按钮虚拟控制台 windows 使用它们各自的 gui 对象,导致两个按钮被绘制在同一个虚拟控制台 window 上。为什么?
是的,Lua 中的 OOP 对于 Lua 初学者来说很难,尽管他们非常了解 OOP 语言(例如 Java)。
--Meta class
gui = {} -- class is a global variable, no default properties exist
function gui:new(t, title) -- t = window, self = your class "gui"
local o = {} -- creating NEW object
setmetatable(o, self) -- link the object with the class
self.__index = self
o.t = t -- save window into object (not into class)
local sX, sY = t.getSize() -- get the size of the virtual terminal
o.vpx = sX -- save window's properties into object (not into class)
o.vpy = sY
t.setCursorPos(1, 1)
t.write(tostring(title))
return o
end
function gui:drawButton(x, y, sX, sY, colour) -- self = object
....
end