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
Другие вопросы по тегам