Haiku API in Python with PyBind11

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:
PyHaiku

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.

5 Likes

I don’t know if it turns up in search results, so have you seen http://haiku.healynet.org/cgi-bin/fossil/haiku-api-bindings/home ? I think this was relatively mature and had some pretty sophisticated examples. Not sure why we don’t have it packaged, actually.

1 Like

Good day everyone,

Well, I’ve been trying to get a hold on those bindings, and they just cover 5 kits (Application, Interface, Kernel, Storage and Support), and some examples “work” better than others.

Actually I didn’t know anything about the Pybind @aarroyoc was talking about, I was trying to use simple and plain calling using <Python.h> with plenty of problems (not a dev here), and for what he says, looks like that it is the piece of the puzzle I need to get the test I’ve been trying to get a success result with failure after failure going. To be honest, partial failure, but the failure was in the most important part.

Will try that approach as soon as I have some time to get my hands on Haiku and will post results here.

Regards,
RR

Maybe this isn’t relevant anymore: BeThon ?
https://wiki.python.org/moin/BeThon
https://vetusware.com/download/Bethon%200.5.1/?id=15596
https://discuss.haiku-os.org/t/python-bindings-for-haiku/2669/2

Good day,
firstAproach

What I’m trying to achieve is what is shown in the previous image, that is, integrating some Python software in Haiku, as running python script.py will show the three cubes icon of the python interpreter, instead of the software icon.

While by the image it might look like that is already achieved, it’s not. Yes, we see the Awesome Icon on the deskbar, we see the Awesome window on the desktop, but this is because the project is being “running logged” from Paladin. I still need to figure out how to go to the next step, and it’s not straightforward (my lack of knowledge also has something to do here too). I’m learning as I do this stuff.

After reading some docs on PyBind11, and without testing anything yet, it seems like a good approach to do Python development exposing the Haiku API to the Python interpreter, instead of using Healynet. Bethon, though older and stalled development, looks like better integrated with the API, at least all functions have the standard API names. Healynet approach differs, and functions are called different, dropping the B.

From what I’ve been looking, reading and trying, there are 2 different “needs” here:

  • A C++ wrapper for python only apps
    I’m thinking here of Manuskript. Which is a python app and runs directly by typing python manuskript.py. In this scenario, I presume (correct me if I’m wrong), that a simple C++ wrapper would do.
  • Creating new software in Python using the Haiku API
    In this scenario, there are two possible options, which are:
    – Still use a C++ wrapper for python software that uses the Healynet python API
    – Use PyBind to expose the C++ Haiku API to python code

I didn’t know that PyBind even existed before reading @aarroyoc’s post, not even Cython. On Gnome, I just launch Gnome-Builder, and it handles everything to genereate a Flatpak from the python code, so never worried about these.
On Haiku is not that simple, and I do really need to learn and dig deeper, and it’s taking longer than expected, though hopefully we’ll get there.

Also, in some cases, some python modules are needed in order for software to run on Haiku. These modules that should be installed with pip, usually fail to install on Haiku throwing errors that I’m yet to understand, making it harder to test stuff.

Achieved milestones will be notified properly in here. And once achieved, I feel there is a need to write a proper tutorial on this. I’m already taking notes in order to “spread” the info later on, once there is success.

Regards,
RR

1 Like

Good day,

For the C++ wrapper, almost there. I finally figured out how to avoid the miriad of errors thrown by Paladin…AwesomePythonCommand
AwesomePaladin
AwesomeCommandLine

Now I just have to figure out why it does not work by double clicking on the app icon. Because this issue makes me feel a bit stressful. This has nothing to do with the PyBind approach provided by @aarroyoc, it’s a totally different thing, almost works.

So if anyone has a hint on why double click does not launch the app, please let me know.

Thanks and regards,
RR

Just guessing, but if anything in there depends on environment factors subsequent to starting Terminal, that’s one obvious reason why it would work in Terminal and not from Tracker. PATH, current working directory, etc.

1 Like

Good day,

I have to start saying thanks a lot @donn. That was the issue, the path to the python script. So finally this small test has proven successful!


Now, double clicking on the Awesome app icon does launch the Awesome App. I need to make a package and install it, because I presume that the python script file will be placed inside a different location, thus breaking the app.

Also I should carry this test with some real software, and if it does work, write the tutorial.

Thanks again @donn.

Regards,
RR

also check if it runs from a symlink such as from deskbar, may have different PATH and working dir

Good day,

At last, this is a small success:
AwesomeInstalled

The wrapper seems to work fine now for this simple “Awesome” app that only holds a TextView (BTextView). An HPKG has been made and the package has been installed. In order to test if it worked, I moved the project folder to another location, and it does work. It does work from the symlink @jjpx, though it took me a bit of time to figure it out.

As this test wast successful in a way, now I need to test again with real software and verify that this process applies. If it does, good thing for bringing Python software to Haiku, if not, well… I learnt something.

Another test to carry out now is using the approach @aarroyoc presented here to make a simple native python app for Haiku. That will be another day.

Thanks all.
Regards,
RR
(tutorial, here:

4 Likes