I really wanted to have native Python apps on Haiku and after looking at some Python-Haiku bindings, I couldn’t find anything mature enough. So I started experimenting with Cython and PyBind11 (both tools already packaged). After some work, my conclusion is that PyBind11 is better.
With some work I get an app running, showing an button that responds to events:
app.py code:
from haiku import BApplication, BWindow, BRect, BMessage, BView, BButton, window_type
class Window(BWindow):
def __init__(self):
BWindow.__init__(self, BRect(100,100,400,400), "Hola PyBind11", window_type.B_TITLED_WINDOW, 0)
self.say_hi = BMessage(1)
self.panel = BView(self.Bounds(), "panel", 8, 20000000)
self.button = BButton(BRect(10,10,50,50), "hi", "Hola", self.say_hi)
self.panel.AddChild(self.button, None)
self.AddChild(self.panel, None)
self.Show()
def MessageReceived(self, msg):
if msg.what == self.say_hi.what:
print("SAY hi!")
else:
BWindow.MessageReceived(self, msg)
def QuitRequested(self):
print("PyQUIT")
return True
class App(BApplication):
def __init__(self):
BApplication.__init__(self, "application/x-python")
self.window = Window()
def main():
a = App()
a.Run()
if __name__ == "__main__":
main()
And wrapper code:
#include <python3.7/pybind11/pybind11.h>
#include <AppKit.h>
#include <InterfaceKit.h>
namespace py = pybind11;
class PyBWindow : public BWindow{
public:
using BWindow::BWindow;
void MessageReceived(BMessage* msg) override {
py::gil_scoped_release release;
{
py::gil_scoped_acquire acquire;
PYBIND11_OVERLOAD(
void,
BWindow,
MessageReceived,
msg
);
}
}
bool QuitRequested() override {
py::gil_scoped_release release;
{
py::gil_scoped_acquire acquire;
PYBIND11_OVERLOAD(
bool,
BWindow,
QuitRequested,
);
}
}
};
PYBIND11_MODULE(haiku,m) {
py::class_<BApplication>(m, "BApplication")
.def(py::init<const char*>())
.def("Run", &BApplication::Run);
py::class_<BMessage>(m, "BMessage")
.def(py::init<uint32>())
.def_readwrite("what", &BMessage::what);
py::class_<BRect>(m, "BRect")
.def(py::init<float, float, float, float>());
py::class_<BView>(m, "BView")
.def(py::init<BRect, const char*, uint32, uint32>())
.def("AddChild", (void (BView::*) (BView*, BView*)) &BView::AddChild);
py::class_<BWindow, PyBWindow>(m, "BWindow")
.def(py::init<BRect, const char*, window_type, int>())
.def("Show", &BWindow::Show)
.def("MessageReceived", &BWindow::MessageReceived)
.def("QuitRequested", &BWindow::QuitRequested)
.def("Bounds", &BWindow::Bounds)
.def("AddChild", (void (BWindow::*) (BView*, BView*)) &BWindow::AddChild);
py::enum_<window_type>(m, "window_type")
.value("B_TITLED_WINDOW", window_type::B_TITLED_WINDOW);
py::class_<BButton, BView>(m, "BButton")
.def(py::init<BRect, const char*, const char*, BMessage*>());
}
Compiled with:
g++ -shared -std=c++11 -fPIC -I/system/develop/headers/python3.7m haiku.cpp -lbe -o haiku.so
And packages installed:
pkgman install python3 pybind11_python3
This is of course VERY incomplete, but I think it’s a good starting point. In real bindings, all classes with all the methods should be mapped. All virtual methods should also be implemented with PYBIND11_OVERLOAD.
I just wanted to share with you this little code.