Создание приложений с wxLua (Часть 1)Подписка на Комментарии к "Создание приложений с wxLua (Часть 1)"

Аватар xxblx

wxLua - враппер (так называемая "обертка") для тулкита wxWidgets, позволяющий на Lua создавать кросс-платформенные приложения с графическим интерфейсом.

Данная запись подразумевает, что читатель уже знаком с Lua. Наличие опыта с wxWidgets приветствуется.
Запись не избавит от необходимости чтения документации и дополнительного самостоятельного изучения примеров. Она написана чтобы снизить порог вхождения, показать базовую структуру и примеры. Иными словами это "Введение в wxLua", которое поможет сделать первые шаги, а не комплексное учебное пособие.

wxLua можно использовать с wxWidgets 2.8.x, 2.9.x и Lua 5.1, 5.2, LuaJIT. Но для каждой версии сборку из исходных кодов придется производить отдельно с указанием версии опцией у cmake.
О сборке wxLua из исходных кодов можно прочитать в официальном install-руководстве.

Начнем с простейшего примера, создадим пустой фрейм (окно) размером 200x200.

local wx = require("wx")

-- создаем фрейм размером 200x200 с заголовком Hello wxLua
frame = wx.wxFrame(wx.NULL, wx.wxID_ANY, "Hello wxLua",
        wx.wxDefaultPosition, wx.wxSize(200, 200),
        wx.wxDEFAULT_FRAME_STYLE)

-- делаем фрейм видимым
frame:Show(true)

-- запускаем основной цикл выполнения программы
wx.wxGetApp():MainLoop()

Для удобства под интерфейс лучше создать отдельную таблицу. Когда у нас 2-3 элемента интерфейса и 1 фрейм, это вроде бы и непринципиально, но когда придется иметь дело хотя бы с несколькими фреймами и парой-тройкой десятков элементов интерфейса, создавать отдельную переменную под каждый элемент станет, как минимум, неудобно и нелогично.
Создадим таблицу с названием UI для интерфейса.

local wx = require("wx")

local UI = {}

-- создаем фрейм размером 200x200 с заголовком Hello wxLua
UI.frame = wx.wxFrame(wx.NULL, wx.wxID_ANY, "Hello wxLua",
        wx.wxDefaultPosition, wx.wxSize(200, 200),
        wx.wxDEFAULT_FRAME_STYLE)

-- делаем фрейм видимым
UI.frame:Show(true)

-- запускаем основной цикл выполнения программы
wx.wxGetApp():MainLoop()

Теперь рассмотрим добавление элементов интерфейса на фрейм.
Элементы интерфейса на фрейме располагаются не "как есть", а в сайзерах. Сайзер - это сетка, таблица, в ячейках которой расположены какие-то элементы интерфейса или другие сайзеры, это позволяет четко структурировать расположение объектов на фрейме.
Добавим в программу вертикальный сайзер (вертикальный подразумевает 1 столбец и несколько строк), в котором расположим текстовое поле (для ввода имени) и кнопку.

local wx = require("wx")

local UI = {}

-- создаем фрейм размером 200x200 с заголовком Hello wxLua
UI.frame = wx.wxFrame(wx.NULL, wx.wxID_ANY, "Hello wxLua",
        wx.wxDefaultPosition, wx.wxSize(200, 200),
        wx.wxDEFAULT_FRAME_STYLE)

-- создаем вертикальный сайзер
UI.sizer = wx.wxBoxSizer(wx.wxVERTICAL)

-- создаем поле для ввода текста и добавляем его в сайзер
UI.textCtrl = wx.wxTextCtrl(UI.frame, wx.wxID_ANY, "Введи имя")
UI.sizer:Add(UI.textCtrl, 0, wx.wxALIGN_CENTER + wx.wxALL, 5)

-- создаем кнопку и добавляем ее в сайзер
UI.button = wx.wxButton(UI.frame, wx.wxID_ANY, "Нажми меня", wx.wxDefaultPosition, wx.wxDefaultSize, 0)
UI.sizer:Add(UI.button, 0, wx.wxALIGN_CENTER + wx.wxALL, 5)

-- размещаем сайзер на фрейме
UI.frame:SetSizer(UI.sizer)

-- делаем фрейм видимым
UI.frame:Show(true)

-- запускаем основной цикл выполнения программы
wx.wxGetApp():MainLoop()

Рассмотрим подробней добавление элемента на сайзер.

UI.sizer:Add(UI.textCtrl, 0, wx.wxALIGN_CENTER + wx.wxALL, 5)
  • UI.textCtrl - элемент который добавляем.
  • 0 - пропорциональность. Если указать не 0, а какое-то натуральное число, элемент будет растянут в соответствии со значением числа. Таким образом, если нет желания видеть текстовое поле или кнопку размером с половину фрейма, пропорциональность стоит указывать 0.
  • wx.wxALIGN_CENTER + wx.wxALL - первое - выравнивание, второе - отступы, wxALL подразумевает наличие отступов со всех четырех сторон.
  • 5 - размер отступа.

Обращаю ваше внимание на термин "отступ". Дело в том, что в документации, в программах-дизайнерах интерфейса, данный параметр называется "border", что дословно с английского переводится как "граница", но при этом, в данном контексте (добавления элемента на сайзер), это слово используется в значении слова "отступ", по этому я использую именно его. Данный параметр указывает величину отступа от элемента в каждую из четырех сторон: влево, вправо, вверх, вниз. В каждую, т.к. у нас wxALL. Если бы было wxLEFT был бы отступ только влево и т.д.
При всем этом, это же слово "border" - "граница" будет использоваться также и когда речь пойдет о style; window_style и window_extra_style, где уже будет использоваться в значении оформления границы (обычная, двойная и т.д.). Так что, будьте внимательны.

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

local wx = require("wx")

local UI = {}

-- создаем фрейм размером 200x200 с заголовком Hello wxLua
UI.frame = wx.wxFrame(wx.NULL, wx.wxID_ANY, "Hello wxLua",
        wx.wxDefaultPosition, wx.wxSize(200, 200),
        wx.wxDEFAULT_FRAME_STYLE)

-- создаем вертикальный сайзер
UI.sizer = wx.wxBoxSizer(wx.wxVERTICAL)

-- создаем поле для ввода текста и добавляем его в сайзер
UI.textCtrl = wx.wxTextCtrl(UI.frame, wx.wxID_ANY, "Введи имя")
UI.sizer:Add(UI.textCtrl, 0, wx.wxALIGN_CENTER + wx.wxALL, 5)

-- создаем кнопку и добавляем ее в сайзер
UI.button = wx.wxButton(UI.frame, wx.wxID_ANY, "Нажми меня", wx.wxDefaultPosition, wx.wxDefaultSize, 0)
UI.sizer:Add(UI.button, 0, wx.wxALIGN_CENTER + wx.wxALL, 5)

-- размещаем сайзер на фрейме
UI.frame:SetSizer(UI.sizer)

-- обрабатываем нажатие кнопки "Нажми меня"
UI.frame:Connect(wx.wxID_ANY, wx.wxEVT_COMMAND_BUTTON_CLICKED,
    function(event)
       
        local helloMsg = ""
       
        -- получаем содержимое UI.textCtrl
        local name = UI.textCtrl:GetValue()
       
        -- если поле пустое, если имя не указано, приветствие будет "Привет, Анонимус!", иначе "Привет, <имя>!"
        if (name == nil) or (name == "Введи имя") or (name == "") then
            helloMsg = "Привет, Анонимус!"
        else
            helloMsg = "Привет, "..name.."!"
        end
       
        -- создаем wxMessageBox с заголовком "Привет", текстом helloMsg
        wx.wxMessageBox(helloMsg,
                        "Привет",
                        wx.wxOK + wx.wxICON_INFORMATION,
                        UI.frame)
    end
)


-- делаем фрейм видимым
UI.frame:Show(true)

-- запускаем основной цикл выполнения программы
wx.wxGetApp():MainLoop()


Рассмотрим подробней создание окна сообщения wxMessageBox:

wx.wxMessageBox(helloMsg,
                "Привет",
                wx.wxOK + wx.wxICON_INFORMATION,
                UI.frame)
  • helloMsg - текст сообщения. Естественно, вместо переменной можно было указать непосредственно желаемую строку, например, "Приветствую, пользователь!".
  • "Привет" - заголовок окна.
  • wx.wxOK + wx.wxICON_INFORMATION - отобразить на окне кнопку "Ок" и информационную иконку.

    Можно было указать "wx.wxYES_NO + wx.wxCANCEL + wx.wxICON_QUESTION" и тогда появившийся wxMessageBox выглядел бы так:

  • UI.frame - родительский фрейм.

На этом данная запись заканчивается. Продолжение следует.
В следующей части рассмотрим создание простейшего текстового редактора с функциями открытия, редактирования и сохранения текстовых файлов.

Отзывы и предложения можно оставить либо здесь в комментариях, либо в теме с данным туториалом на моем личном форуме.

Похожие материалы:
Аватар dnsis

День добрый. Второй год жду второй части статьи. Интересная для меня тема. Не могу понять как пользоваться справкой wxLua. Запнулся в 2 местах при изучении. Может поможете.
Вот добавляю 2 меню.

ui.testMenu = wx.wxMenu("", wx.wxMENU_TEAROFF)
ui.testMenu:Append(wx.wxID_ANY, "&Test 1", "Test one")
ui.testMenu2 = wx.wxMenu("", wx.wxMENU_TEAROFF)
ui.testMenu2:Append(wx.wxID_ANY, "&Text 2", "Test two")
ui.menuBar = wx.wxMenuBar()
ui.menuBar:Append(ui.testMenu, "&Test one")
ui.menuBar:Append(ui.testMenu2, "&Test two")
-- обработка
ui.frame:Connect (wx.wxID_ANY, wx.wxEVT_COMMAND_MENU_SELECTED,
    function (event)
        ...
    end

Как обработать событие, что бы понять какой пункт меню был выбран?
Спасибо.

Аватар xxblx

Для разных пунктов меню сделать разные ID'ы.

ID_TEST1 = 1001
ID_TEST2 = 1002

ui.testMenu = wx.wxMenu("", wx.wxMENU_TEAROFF)
ui.testMenu:Append(ID_TEST1, "&Test 1", "Test one")
ui.testMenu2 = wx.wxMenu("", wx.wxMENU_TEAROFF)
ui.testMenu2:Append(ID_TEST2, "&Text 2", "Test two")
ui.menuBar = wx.wxMenuBar()
ui.menuBar:Append(ui.testMenu, "&Test one")
ui.menuBar:Append(ui.testMenu2, "&Test two")
-- обработка
ui.frame:Connect(ID_TEST1, wx.wxEVT_COMMAND_MENU_SELECTED,
    function (event)
        -- здесь обработается выбор пункта меню "Test 1"
    end
)

На счет 2-ой части, я давно уже не работаю с wxLua, по этому и писать дальше не стал.
wxLua оправдывает себя только когда другого выбора нет. В других случаях лучше присмотреться к другим решениям, например, к wxPython.

Аватар gNox

Мне тоже понравилась ваша статья и описанная технология. Стал всматриваться и изучать.

Попутно возник вот какой вопрос:

Сейчас wxLua разрабатывается и поддерживается одним человеком. Помимо того, что последний релиз был в 2012-м, за последний год автор сделал ровно два коммита и отвечает на вопросы юзеров раз в несколько месяцев.

Как по вашему мнению, стоит ли полагаться на wxLua в серьёзных разработках на Lua или автор отошёл от него и проект умирает?

Аватар xxblx

gNox написал:

Как по вашему мнению, стоит ли полагаться на wxLua в серьёзных разработках на Lua или автор отошёл от него и проект умирает?


Однозначно нет.
wxLua стоит использовать:
1. из спортивного интереса / "а потому что могу!" / просто для практики
2. возможно для знакомства с wxWidgets за пределами "плюсов и питона"
3. когда у вас уже есть что-то на Lua или что-то, использующее Lua с возможностью загружать любые модули, и другого варианта прикрутить, например, GUI нет, а GUI очень хочется.

В иных случаях (то есть практически всегда) лучше не использовать wxLua, тем более в серьезных проектах.

Если интересует именно wxWidgets, лучше обратить внимание на wxPython. Он, конечно, тоже не очень-то активно развивается и до сих пор не был нормально портирован на Python3, но, в целом, процесс идет и работать с ним можно.

Я, если проект свободный, на сегодняшний день предпочитаю брать связку Python3 + PyQt5, но если проект проприетарный придется покупать лицензию (у PyQt двойное лицензирование, под GPL для свободных проектов, а для проприетарных платная коммерческая лицензия).
Реже сейчас использую wxPython и Python 2, там не так всё свежо, зато лицензия позволяет использовать wxPython в любых проектах без покупки чего-либо. Ну и плюс, у Python 2 поддержка до 2020 года, а wxPython постепенно переписывается на Python 3, так что, особых проблем быть не должно, проект "не протухнет".

Аватар Александр

Статья очень интересная!
Почему то ненашёл продолжения - надо продолжить, я думаю
что библиотека wxLua будет безусловно востребована.

Добавить комментарий