Lua (CC) Класс GUI рисует все компоненты в одном окне, когда им приказано рисовать их в отдельных окнах
прелюдия
ComputerCraft - это мод для Minecraft (Forge), который добавляет в игру грубый компьютер на основе lua. Используя этот компьютер, можно писать программы для взаимодействия с миром Minecraft различными способами. Вопрос о том, применим ли вопрос ComputerCraft к Stackru, ранее уже обсуждался в других вопросах, но я считаю, что он применим, поскольку мод, по большей части, предназначен для программирования, и, хотя выполняются некоторые вызовы API-интерфейса ComputerCraft, никакой концепции не существует. в этом вопросе, который не будет применяться к другим не связанным с ComputerCraft программам lua (если, конечно, проблема не вызвана ошибкой в самом ComputerCraft). Документацию по используемым API можно найти по адресу http://www.computercraft.info/wiki/Category:APIs.
Примечание: не пугайтесь, если у вас нет опыта работы с ComputerCraft; Я полагаю, что эта проблема может быть совершенно не связана с ComputerCraft, а вместо этого вызвана некоторой сложностью ООП в lua, которую я не смог понять. Я прокомментировал код, где я счел необходимым объяснить наиболее важные аспекты запатентованных звонков, которые я делаю. Если что-то неясно, пожалуйста, прокомментируйте, и я уточню.
Если вы хотите иметь возможность запускать примеры кода без Minecraft, есть отличный эмулятор ComputerCraft, который называется CCEmuRedux. Я проверил свой код на обоих компьютерах ComputerCraft и CCEmuRedux с идентичными результатами, хотя CCEmuRedux, похоже, не поддерживает мониторы. "Расширенный" компьютер необходим, чтобы увидеть цвета.
проблема
В ComputerCraft 1.75 (и CCEmuRedux @ ComputerCraft 1.79) с учетом следующего графического интерфейса пользователя и тестовой программы, которая пытается нарисовать элементарную кнопку в каждом из двух разных окон с помощью класса gui, обе кнопки отображаются во втором окне. Графически результат guiTest.lua - https://i.imgur.com/llFDlYI.png, хотя я ожидаю, что первая (оранжевая) кнопка будет нарисована в окне 1. Хотя у меня есть некоторые теории относительно того, почему она ведет себя таким образом, у меня нет необходимого опыта Луа, чтобы выяснить, как это исправить. Это 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, поэтому я ожидаю, что это будет "легкой" проблемой. У меня есть более чем базовое понимание того, как ООП работает на нескольких других языках (в частности, на Java), и поэтому "Spidey-Sense" моего программиста говорит мне, что какая-то переменная, такая как t, не является "достаточно локальной" msgstr " (одна и та же переменная используется обоими окнами), или некоторая ссылка в одном из объектов графического интерфейса перезаписывается при создании нового графического объекта.
Поэтому я попытался сделать таблицу GUI локальной, чтобы она не перезаписывалась:
local gui = {t, vpx, vpy}
... но это выплюнуло ошибку attempt to index ?
в строке 6 "gui.lua" (setmetatable(o, self)
), поэтому вместо этого я попытался (понимая, что я не смогу получить доступ к функции извне 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 - это "Исключительно запутанный ™", но я предполагаю, что суть в том, что "вы не можете сделать метод объекта локальным", как я могу сделать другие функционирует локально аналогично тому, что я пробовал здесь.
Это не проблема с window.create()
или с использованием оконного API в целом, как то же самое происходит при использовании отдельных мониторов вместо отдельных окон на одном мониторе. По существу:
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"...?
... или, возможно, более вероятно, проблема - то, что я полностью пропустил.
Заключение
Короче говоря, если задать два коротких вопроса, определив два объекта графического интерфейса, по одному для каждого из двух окон виртуальной консоли, и попытаться нарисовать одну кнопку в каждом из окон виртуальной консоли, используя соответствующие им объекты графического интерфейса, в результате обе кнопки будут нарисованы в одном виртуальном консольное окно. Зачем?
1 ответ
Да, ООП в Lua сложно для начинающих, несмотря на отличное знание языков ООП (таких как 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