Как написать калькулятор на lua
Оболочка программы iProgPro поддерживает выполнение скриптов, написанных на языке Lua.
Программа имеет встроенный редактор для скриптов Lua.
Если у вас не открыто дерево калькуляторов, то необходимо открыть его через главное меню Вид-Калькуляторы.
Возможно у вас уже есть какие-то калькуляторы Lua (они имеют расширение *.lua ) :
Калькулятор Lua может не иметь окна. Тогда он просто выполняется каждый раз, когда происходит двойной клик на его имени в списке.
Что бы открыть файл для редактирования, необходимо выбрать его в списке, затем вызвать выпадающее меню правой кнопкой мыши и выбрать в этом меню » Открыть исходный файл «
Файл для калькулятора будет открыт для редактирования прямо в редакторе программы как текстовый файл.
Внимание : для открытия калькулятора для редактирования в программе уже должен быть активным редактор. Если выбран скрипт, в котором не отображается редактор, калькулятор на редактирование не откроется.
Для создания нового скрипта необходимо выбрать любой скрипт в дереве скриптов, вызвать выпадающее меню и выбрать в нем «Создать новый файл в этой папке»
Появится окно с приглашением ввести имя файла.
После ввода имени файла отображается пустое окно редактора калькулятора.
Теперь нажмите клавиши Ctrl + S для сохранения файла калькулятора. Затем сделайте двойной клик на имени вашего калькулятора в списке калькуляторов. Окно калькулятора появится на экране.
Можно нажимать на кнопку калькулятора, но ничего не будет происходить, так как кнопке не назначено никакое событие.
Закройте калькулятор, в окне редактора выберите закладку Source и скопируйте туда текст :
function click(id)
print(‘Button clik! > end
Если редактор свойств не отображается, нужно кликнуть мышкой на изображение кнопки в визуальном в редакторе (внутри серой рамки).
Запустите калькулятор снова (двойной клик на имени файла в списке калькуляторов). Появляется окно калькулятора и при нажатии на кнопку в окне калькулятора в окне сообщений выводится строка
Для взаимодействия конкретно со программой iProg Pro в язык добавлены некоторые функции (их нет о стандартной Lua ) :
print(string)
ShellExecute(string)
SetCaption(string)
Close()
http ()
local Status, data = http(‘ya.ru’,’GET’,»);
print(‘Status = ‘..Status);
print(‘data = ‘..data);
Функция возвращает два параметра :
В данном примере ответ был такой :
Дальше данные обрезаны.
DeviceID()
uid,sn = DeviceID()
print(‘sn = ‘..sn)
print(‘uid = ‘..IntToHex(uid))
sn = 383
uid = F28E56AC
Функция возвращает значение, которое зависит от кнопки, которую нажал пользователь в окне. Коды возвращаемых значений здесь
function OnEditorSelect(start,finish)
Memo1.Clear()
Memo1.Add(‘Адреса: ‘..start..’. ‘..finish)
End
Обработчик On LoadFile
function OnLoadFile()
Memo1.Clear()
Memo1.Add(‘ В редактор загружен файл! ‘)
End
Обработчик O nSelectFile
function OnSelectFile(filename)
Label2.SetText(‘Выбран файл: ‘..filename)
end
Взаимодействие с редактором скрипта
Все функции работают с редактором, который отображается в данный момент на экране. Если выбран скрипт без редактора, то функции работы с редактором игнорируются в калькуляторе.
Если происходит обращение за пределы редактора, то запись в редактор игнорируется, чтение возвращает неопределенное значение.
value = Editor.Read(25)
adr = 53 4
value = Editor.Read(adr)
Пример (байт по адресу 0 будет посвечен (по умолчанию красным цветом)) :
Editor.SetHighlight(0,1)
aaa = Editor.Highlight(0)
print(‘ Адрес курсора : ‘..Editor.Position())
Визуальный редактор позволяет быстро создавать и редактировать визуальные элементы в окне калькулятора.
Основную часть справа занимает рабочая область визуального редактора, ограниченная серой рамкой. Левую и нижнюю границу рамки можно захватить мышью (рамка станет красной) и перемещать, изменяя размер окна.
Справа вверху редактора расположена панель визуальных компонентов, которые может содержать окно калькулятора.
Для размещения компонента в окне нужно кликнуть на нем мышкой на панели, затем кликнуть мышкой рабочей области окна визуального редактора. При выделении компонента на его рамке отображаются черные квадратики. Ухватив мышкой эти квадратики можно изменять размер компонента. Перемещать компонент можно, кликнув на нем левой кнопкой мыши и удерживая ее нажатой перемещать мышь.
Для удаления компонента необходимо выбрать его в списке в левой верхней части редактора и нажать клавишу Del на клавиатуре.
Alignment
Caption
ID
Name
OnClick
Icon
Используется для однострочного редактора. Текст, который будет по умолчанию отображаться в редакторе
Доступ к визуальным компонентам из кода калькулятора.
Для обращения к какому-либо свойству компонента необходимо записать его имя, заданное в визуальном редакторе, затем через точку имя функции, которая устанавливает или считывает свойство компонента.
Color()
FontColor()
aaa = Label.FontColor()
Label.SetColor(0xFF00FF)
Label.SetColor( 500 )
fn = Label.FontName()
Button.SetFontName(‘Courier’)
fs = Label.FontSize()
Button.SetFontSize( 22 )
П ример (Button- имя компонента ):
Button.Enable()
Button.Disable()
bt = Button.Text()
lt = Label.Text()
Memo1.Clear()
Memo1.Add(‘Memo строка 1 ‘)
Memo1.Add(‘Memo строка 2 ‘)
Memo1.Add(‘Memo строка 3 ‘)
Combobox1.Clear()
Combobox1.Add(‘ Выпадающий список строка 1 ‘)
Combobox1.Add(‘ Выпадающий список строка 2 ‘)
Для числового редактора типа Digit возвращает значение (число), отображаемое в редакторе
Для выпадающего списка возвращает индекс выбранной строки (индексы начинаются с нуля)
Для шестнадцатеричного редактора возвращает отображаемое значение в виде числа. Если в редакторе отображается более 4-х байт, то возвращается значение 4 левых байт.
Для числового редактора типа Digit устанавливает значение в редакторе
Для выпадающего списка устанавливает индекс выбранной строки (индексы начинаются с нуля). Если value имеет недопустимое значение (меньше нуля или больше количества строк), то функция игнорируется.
Для шестнадцатеричного редактора устанавливает отображаемое значение. Если в редакторе отображается менее 4 байт, то старшие биты значения отбрасываются. Если в редакторе отображается более 4 байт, то значение устанавливается для 4 левых байт
Вызов функций в калькуляторе
Функция для обработки OnClick должна обязательно иметь следующий заголовок :
При вызове функции он будет содержать ID компонента, который вызвал эту функцию.
При запуске калькулятора Lua до появления окна калькулятора на экране выполняется весь код, записанный снаружи процедур.
function aaa()
print(‘. ‘)
end
function bbb()
print(‘. ‘)
end
function load()
print(‘Код внутри процедуры load()’)
end
При запуске такого калькулятора будут выведены сообщения :
Версия программы и папка калькулятора
Специальная переменная SW_VERSION содержит версию программы, на которой исполняется скрипт. Переменная CURRENT_PATH содержит имя папки, в которой находится запущенный калькулятор
print(‘Версия софта: ‘..SW_VERSION)
print(‘Папка: ‘..CURRENT_PATH)
Версия софта: 65
Папка: E:\iProg Pro\Calculators\lua\
LXF122:LUA
Подписка на печатную версию Весь 2015 год (12 номеров) Первое полугодие (6 номеров) Второе полугодие (6 номеров) | Подписка на электронную версию Весь 2015 год (12 номеров) Первое полугодие (6 номеров) Второе полугодие (6 номеров) | Подшивки старых номеров журнала (печатные версии) Весь 2014 год (12 номеров) Первое полугодие (6 номеров) Второе полугодие (6 номеров) |
Содержание
Lua: Что в нем такого?
Знаете, что общего у игр SimCity, World of Warcraft, Far Cry, приложения Adobe Lightroom, web-сервера Apache и «железного» робота Crazy Ivan? Все эти программы и устройства, такие большие и такие разные, используют Lua в качестве встроенного языка сценариев.
Lua (что в переводе с португальского означает «Луна») разрабатывается на факультете компьютерных наук Епископального католического университета Рио-де-Жанейро (Pontifical Catholic University of Rio de Janeiro). Коварные программисты-католики, пытающиеся захватить мир с помощью языка программирования сценариев – чем не тема для очередной нетленки Дэна Брауна? Дарю ему эту идею, а мы с вами займемся вещами куда менее мистическими.
Познакомимся ближе
Логотип Lua. Нет ли здесь скрытого подтекста?
Тот факт, что Lua предназначен для встраивания в другие программы, серьезно повлиял на структуру самого языка. Здесь нет понятий главной функции вроде main() и основной программы, вместо этого говорят о «среде», где хранится то, что должно быть доступно глобально. Базовой структурной единицей Lua является набор операторов – chunk (для ясности, будем говорить «фрагмент»), который представляет собой. просто набор операторов. Фрагмент Lua не имеет специального оформления начала и конца. Он начинается с первым оператором и оканчивается с последним. При этом фрагмент может вводить локальные переменные и возвращать значения с помощью оператора return. Каким же образом один фрагмент отделяется от другого? Естественным разделителем служит сама программа-хозяин. Например, сценарии, загружаемые ею в ответ на действия пользователя, могут быть оформлены как фрагменты Lua.
Помимо фрагмента, в Lua есть понятие блока [block]. Блок – это фрагмент, границы которого выделены специальными операторами, например, do и end. Блоки используются там, где требуется вложить один фрагмент Lua в другой, и управляют видимостью локальных переменных, а также действием операторов вроде break. Операторы ветвления и цикла тоже используют блоки.
Игровые проекты уже давно достигли той степени сложности, когда требуется разделение «базиса» и «надстройки» – движка, определяющего свойства игрового мира, и правил, которым подчиняются его элементы. Обычно движок является наиболее стабильной частью игры, а система, описывающая правила поведения персонажей и предметов, наоборот, подвержена частым изменениям. И если определять правила с помощью языка программирования сценариев, разработчикам не придется переделывать движок при каждом изменении правил поведения персонажа или свойств артефакта. Эти же аргументы применимы и к роботам, причем не обязательно к большим и человекоподобным.
Встроенный язык бесполезен, если он не способен обмениваться данными с программой-хозяином. И, конечно, в Lua предусмотрена возможность передачи информации к «родителю» и от него, а также вызов определенных в нем функций.
Переменные объявляются без указания типа (он устанавливается динамически в момент присваивания значения). Всего в Lua насчитывается восемь типов: логический, строка, число, функция, поток, пользовательские данные, таблица и тип nil (указывает, что переменной не присвоено никакое значение; эквивалентен false в логических выражениях).
Численный тип в Lua амбивалентен. Число может быть целым, с плавающей точкой и шестнадцатеричным. По умолчанию для внутреннего представления чисел применяется double, но при желании нетрудно заменить его на любой другой тип (гибкость подстройки – одна из очень полезных отличительных характеристик Lua). Строки в Lua представляют собой массивы восьмибитовых символов произвольной длины; ‘\0’ не имеет специального значения и может встречаться в любом месте строки.
Тип «функция» – это почти то же самое, что и указатель на функцию в C. Потоки используются для реализации так называемых со-процедур, которые могут выполнятся параллельно. Следует сразу отметить, что многозадачность со-процедур в Lua носит добровольный (корпоративный) характер, то есть переключение с одной процедуры на другую осуществляется явным образом, с помощью вызова специальной функции yield. Физически код Lua выполняется в одном потоке (так, по крайней мере, обстоит дело в реализации по умолчанию). Ничто, однако, не мешает запустить параллельно несколько интерпретаторов Lua в одной программе-хозяине.
Пользовательский тип данных предназначен для обмена информацией с «родителем». Обычно данные этого типа имеют смысл только для программы-хозяина и непрозрачны для Lua. Над ними можно выполнять только две операции: присваивание и проверку тождества.
На таблицы следует обратить особое внимание, поскольку это единственный доступный в Lua сложный тип данных. Таблицы Lua представляют собой ассоциативные массивы (то есть в качестве индекса в них можно использовать любые значения). Учитывая абсолютный полиморфизм переменных Lua, элементы ассоциативных массивов также могут содержать любые значения. Все это делает таблицы Lua чрезвычайно мощным средством для эмуляции таких метатипов, как структуры и объекты.
Правила выполнения операций с данными определяются в Lua с помощью метатаблиц. Вы можете создавать метатаблицы для определенных вами таблиц, но не можете менять метатаблицы, определенные для простых типов данных Lua (изменить правила обращения с последними можно через C API).
Все вышесказанное приводит нас к еще одной важной особенности Lua – гибкому, расширяемому синтаксису языка.
Единственными перечисляемыми типами в стандартном Lua являются численный и логический; char здесь отсутствует. Выражение «A» – это строка из одного символа, а не значение типа «символ». Эти ограничения не являются фатальными, но если вы привыкли писать программы на C/C++, вам придется несколько изменить стиль программирования.
Наша первая программа
Пришла пора взглянуть на Lua в действии. Любой дистрибутив Lua – это, прежде всего, набор библиотек, подключив которые к своему приложению, вы получаете возможность использовать в нем сценарии Lua. В дистрибутив также входит программа lua, которую можно рассматривать как интерпретатор Lua (хотя, строго говоря, она ничем не отличается от других программ, использующих Lua как встроенный язык), и luac – компилятор Lua, но его мы пока рассматривать не будем.
Хотя Lua может использоваться как обычный интерпретируемый язык программирования, он создавался именно для встраивания в приложения C/C++. Неудивительно, что многие разработчики выбрали его для реализации системы сценариев в своих проектах. От других аналогичных языков (например, Python) Lua отличается компактностью и высоким быстродействием интерпретатора и компилятора времени выполнения. Еще одно преимущество Lua – простота, с которой к программе можно «прицепить» сторонние библиотеки, написанные на C/C++.
Библиотек в дистрибутиве обычно великое множество. У Lua есть своя система для создания графического интерфейса IUP, использующая (в зависимости от ОС) Motif, GTK+ или GDI+. Библиотека Canvas Draw позволяет работать с двумерной графикой на любой из поддерживаемых платформ. Для обработки растровых изображений в популярных форматах служит библиотека IM. LuaCURL, как подсказывает название, является оберткой Lua для CURL (http://curl.haxx.se). С ее помощью программы, написанные на Lua, можно без труда наделить поддержкой клиентской части популярных интернет-протоколов, а с помощью Copas программу Lua можно превратить и в TCP/IP-сервер. Библиотека LuaSQL предназначена для взаимодействия с распространенными СУБД, а LPeg позволяет выполнять сравнение с образцом, используя специальный язык описания синтаксиса.
Богатство библиотек Lua не исчерпывается перечисленными. Особого внимания заслуживает также Alien, предназначенная для взаимодействия со сторонними разделяемыми библиотеками.
В интерпретируемых языках программа «Hello World» может состоять из одной-единственной строчки; Lua здесь не является исключением:
Сохраните этот текст в файле helloworld.lua и скомандуйте
В результате на экране консоли вы увидите то, что и ожидали.
Между прочим, в Unix-системах интерпретатор lua можно активировать с помощью #!-строки. Если переписать программу «Hello World» в виде
то файл helloworld.lua можно отметить как исполняемый и запускать самостоятельно.
Рассмотрим более сложный пример:
Программа сначала просит вас ввести свое имя, а потом вежливо здоровается. Разберем ее построчно. Функция write(), объявленная в стандартной библиотеке io (имя библиотеки указывается как префикс, отделенный точкой), подобна print(). Одно из отличий заключается в том, что write() не выполняет автоматический перевод строки по окончании вывода. Функция read() считывает данные из стандартного потока ввода. Обратите внимание: мы ничего не говорим read() о типе данных, которые она должна считать. Ее задача – получить символы, а интерпретатор Lua разбирается с тем, что они означают. Введенная строка сохраняется в переменной name. Для объявления переменной достаточно просто ввести ее имя в соответствующем контексте. Как было отмечено выше, тип переменной не указывается. Подобно C, имена переменных в Lua регистро-зависимы (‘name и Name – разные вещи); то же самое относится и к другим синтаксическим элементам языка. Оператор .. позволяет объединить две строки или строку и число в одну. Обратите внимание, что при задании строки можно использовать как двойные, так и одиночные кавычки. Строки Lua поддерживают тот же набор спецсимволов (\n и так далее), что и C. Как вы могли заметить, символы, разделяющие выражения Lua, необязательны, но можно использовать оператор ;, как это делается в C:
Пустой оператор ;; здесь недопустим. Давайте сразу договоримся не использовать ; в статьях этой серии.
Понравилось? Вот вам немного синтаксического сахара на закуску:
В результате выполнения этой программы будут напечатаны числа
Множественное присваивание, когда слева от оператора = перечислено несколько имен переменных, а справа – несколько значений, одна из характерных «фишек» Lua. Одновременное присваивание сделано не ради пустого оригинальничания. Вот как в Lua можно выполнить обмен значениями между двумя переменными (другие языки в общем случае требуют для этого третью, временную):
Число переменных слева от оператора = и число значений справа от него могут различаться. Если переменных слева больше, «лишним» будет присвоено значение nil; если справа больше значений, «лишние» будут проигнорированы. В отличие от C, операция присваивания в Lua не возвращает значений, иначе говоря, нельзя использовать конструкцию
Немного математики
Посмотрим теперь, как можно написать на Lua мою любимую программу для вычисления чисел Фибоначчи:
С пониманием цикла while не должно возникнуть проблем (если, конечно, Lua – не ваш первый язык программирования). Фрагмент программы, заключенный между операторами do и end (напомню, он называется блоком), будет повторяться до тех пор, пока истинно проверочное условие цикла. Способность Lua выполнять одновременно несколько присваиваний позволила нам записать рекуррентную формулу вычисления очередного числа Фибоначчи в одну строку. Можно сделать это еще лаконичнее:
Любопытно, что строка
даст тот же самый результат. То есть одновременное присваивание в Lua выполняется следующим образом: сначала вычисляются все выражения, стоящие справа от = (это происходит в порядке их перечисления), а затем производится присваивание значений. Из этого следует, что при обмене значений между переменными
на самом деле используется две скрытых переменных для хранения правых значений (а не одна явная, как в классическом алгоритме). Одновременное присваивание – не такое уж одновременное! На самом деле это просто синтаксическое удобство. Кстати, в Lua нет аналогов операторов C, ++ и —.
Иногда полиморфизм переменных Lua способен привести к неожиданным и неприятным результатам. Рассмотрим такой фрагмент программы:
Оператор # позволяет узнать размер переменной, которой может быть присвоено значение произвольной длины (например, строка). В нашем случае фрагмент программы напечатает число 2 (длина строки, присвоенной переменной x). Если по аналогии мы напишем
интерпретатор выдаст сообщение об ошибке – попытке вычислить длину числовой переменной. Все дело в том, что строка, состоящая из одних цифр, автоматически преобразуется в процессе присваивания в число, а к переменным, содержащим числа, оператор # неприменим. С ним нужно обращаться осторожно и не применять его в том случае, если вы не уверены, какое значение содержит переменная. Если подобное поведение оператора # представляется вам нелогичным, наберитесь терпения. Далее мы покажем вам, как, погрузившись в дебри C, вы сможете изменить его (при этом, конечно, у вас появится своя собственная версия языка Lua, не совсем совместимая с другими). Вот еще пример:
В результате выполнения этого фрагмента будут выданы два значения: 12 и 3. Первое представляет собой результат конкатенации двух переменных (напомню, что оператор .. одинаково работает со строками и числами), второе – их сумму. Если же вместо этого мы напишем
то попытка вычислить выражение x+y приведет к ошибке «применение арифметического оператора к строковым значениям».
Все переменные, которые мы объявляли до сих пор, были глобальными, то есть видимыми во всех фрагментах программы, выполняемой данным экземпляром интерпретатора. С помощью ключевого слова local можно объявлять локальные переменные, доступные только внутри текущего фрагмента или блока.
Дела табличные
Теперь познакомимся с самым интересным типом данных – таблицами. Вообще-то мы займемся ими вплотную в следующей статье, а сегодня рассмотрим, как с их помощью объявлять простые массивы. Перепишем программу вычисления чисел Фибоначчи следующим образом:
Конструкция fib = <> сообщает, что переменная fib представляет собой таблицу, то есть ассоциативный массив. Объявляя ее, мы сразу же задаем две пары «ключ–значение» – fib[0] = 1, fib[1] = 1. В объявлении переменной fib ничто не указывает ни длину массива, ни тип хранимых ключей и значений (он может быть любым). Тот факт, что при объявлении мы присвоили переменной fib какие-то данные, никак не ограничивает нашей свободы в дальнейших манипуляциях с fib, что мы сейчас и покажем.
Но сначала несколько слов об операторе for. Как вы уже поняли, это еще одна разновидность операторов цикла, используемых в Lua. В нашем примере i – переменная-итератор, 2 – начальное значение переменной, 10 – конечное значение, 1 – инкремент. Таким образом, следующий за оператором for блок операторов будет повторен 9 раз. В представленной выше форме оператора for переменная-итератор может быть только числом. В Lua существует и другой вариант оператора for, который позволяет работать с произвольными итераторами с помощью специальных функций. Мы рассмотрим его позже.
делает две вещи: создает новый элемент ассоциативного массива с ключом i и присваивает ему значение (таким образом, до выполнения цикла for массив fib состоит из 2‑х элементов, а после выполнения цикла – из 11‑ти). Это очень важная особенность таблиц Lua. Если переменная var содержит значение типа «таблица», то любая конструкция вида var[exp] (где exp – выражение, результатом которого является значение одного из простых типов) является легальной, независимо от того, существует соответствующий элемент массива или нет. Выражение
создаст новую пару «ключ–значение», а выражение fib[3.14] вернет значение nil, если, конечно, с этим ключом не было уже связано какое-то значение. Если ключ данного элемента массива является строковым значением, то вместо
Такой синтаксис широко применяется в тех случаях, когда таблица используется в роли структуры или объекта.
Выше я уже жаловался вам на поведение встроенного оператора #. Пожалуюсь еще раз: он применим к таблицам, но работает с ними по несколько странным правилам. Если t – переменная, содержащая таблицу, операция #t возвращает целочисленное значение i, такое, что t[i] не равно nil, а t[i+1] равно nil. Перебор подходящих значений начинается с единицы. Для таблицы
выражение #t вернет значение 3, что соответствует числу элементов массива. А вот для таблицы
это же выражение вернет значение 1, то есть посчитан будет только элемент t[1], хотя все три элемента таблицы существуют. Элементы с нецелочисленными ключами тоже, естественно, игнорируются. Если таблицы могут динамически увеличиваться, то, наверное, могут и уменьшаться? Разумеется. Чтобы удалить элемент таблицы, достаточно присвоить значение nil соответствующему ключу:
В заключение знакомства с массивами рассмотрим один «каверзный» пример. Такие конструкции едва ли встречаются в реальном коде, по крайней мере, у вменяемых программистов, но их очень любят авторы всевозможных тестов на знание языка. Пусть u и v – две таблицы. Как будет выполняться присваивание в следующем примере?
Каверза в том, что, как мы знаем, сначала вычисляются выражения, расположенные справа от оператора присваивания – из чего можно сделать вывод, что значение индекса при переменной u будет равно 2; но это не так. Присваивание переменным новых значений происходит после вычисления всех выражений, в том числе и выражений индексов (как слева, так и справа от оператора =), поэтому на момент вычисления индекса u[i+1] значение i равно 0. Таким образом, после выполнения приведенной выше операции элементу u[1] будет присвоено значение v[0], а переменная i получит значение 1.
На закуску
Калькулятор на Lua считает без ошибок.
Дабы у вас не складывалось впечатление, что Lua – скучный и своенравный язык программирования, рассмотрим некоторые его серьезные возможности. При такой легкости интеграции с C/C++ неудивительно, что для Lua сделано множество оберток и привязок. Программы, написанные на Lua, могут использовать для построения интерфейса wxWidgets, GTK+ (напрямую, минуя IUP), Qt (следует особо отметить библиотеку QtLua, которая может использоваться как альтернатива QtScript с его JavaScript-подобным языком ECMAScript), FLTK, FOX и даже ncurses. Этим набором интерфейсы Lua отнюдь не ограничиваются. Более подробную информацию о дополнениях и расширениях стандартной поставки Lua вы найдете на сайте http://lua-users.org. В качестве примера приведем расширение Lua для wxWidgets – wxLua (http://wxlua.sourceforge.net). В состав пакета wxLua входит интегрированная среда разработки для Lua и множество примеров программ с интерфейсом wxWidgets. Результат его работы можно видеть на рисунке.
Хотя в основе своей Lua не является объектно-ориентированным языком, взаимодействие с объектами wxWidgets получается у него отлично.