Lua (CC) Класс GUI рисует все компоненты в одном окне, когда им приказано рисовать их в разных окнах - PullRequest
0 голосов
/ 27 октября 2018

Prelude

ComputerCraft - это мод для Minecraft (Forge), который добавляет в игру грубый компьютер, основанный на луа. Используя этот компьютер, можно писать программы для взаимодействия с миром Minecraft различными способами. Вопрос о том, применим ли вопрос ComputerCraft к StackOverflow, ранее обсуждался и в других вопросах, но я считаю, что применимо , так как этот мод, по большей части, предназначен для программирования, и хотя некоторые вызовы проприетарного 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 и тестовая программа, которая пытается нарисовать элементарную кнопку в каждом из двух разных окон, используя 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 , недостаточно «локально» (одна и та же переменная используется обоими окнами) или какая-либо ссылка в одном из объектов gui перезаписывается при создании нового объекта gui .

Поэтому я попытался сделать таблицу 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"...?

... или, что более вероятно, проблема - это то, что я полностью упустил.

Заключение

Чтобы сделать длинный вопрос коротким, определив два объекта gui ,по одному для каждого из двух окон виртуальной консоли, и попытка нарисовать одну кнопку в каждом из окон виртуальной консоли, используя соответствующие им объекты gui , приводит к тому, что обе кнопки рисуются в одном и том же окне виртуальной консоли.Почему?

1 Ответ

0 голосов
/ 27 октября 2018

Да, ООП в 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
...