wjakob / nanogui

Minimalistic GUI library for OpenGL
Other
4.66k stars 608 forks source link

Screen without GLFW initialization in Python #344

Closed arbu closed 6 years ago

arbu commented 6 years ago

I'm trying to add nanogui to an existing python application. The application already sets up GLFW. I wanted to override the Screen constructor to not set up GLFW as described in the docs: https://nanogui.readthedocs.io/en/latest/api/class_nanogui__Screen.html#_CPPv2N7nanogui6Screen6ScreenEv so it would like this:

class UnmanagedScreen(nanogui.Screen):
    def __init__(self, glfwWindow):
        self.initialize(glfwWindow, False)

But apparently the initialize function is not implemented in the python bindings. Is there another way to set the GLFW window of the Screen object from python or is this not supposed to be done?

arbu commented 6 years ago

So I looked further into it and found out that the glfw library loaded by nanogui will probably not work with the glfw library loaded in python through cffi. I tried to extract the pointer to GLFWwindow from the glfw library loaded via cffi and pass it to nanogui but that results in the X server returning "Bad window" so there seems to be a problem with the glfw library loaded by nanogui not having access to the window created by the other library. If somebody has any idea how to get this setup working I would be glad about a comment. But since this is probably out of scope of this bug tracker I will close this issue.

svenevs commented 6 years ago

Hey @arbu, this may be easier to solve than you think. The following diff can expose the empty constructor Screen::Screen() (to use as nanogui.Screen()), and Screen::initialize():

diff --git a/python/widget.cpp b/python/widget.cpp
index e1a80f4..4b1d356 100644
--- a/python/widget.cpp
+++ b/python/widget.cpp
@@ -99,6 +99,8 @@ void register_widget(py::module &m) {
             py::arg("size"), py::arg("caption"), py::arg("resizable") = true, py::arg("fullscreen") = false,
             py::arg("colorBits") = 8, py::arg("alphaBits") = 8, py::arg("depthBits") = 24, py::arg("stencilBits") = 8,
             py::arg("nSamples") = 0, py::arg("glMajor") = 3, py::arg("glMinor") = 3, D(Screen, Screen))
+        .def(py::init<>(), D(Screen, Screen_2)) /* empty constructor */
+        .def("initialize", &Screen::initialize, D(Screen, initialize))
         .def("caption", &Screen::caption, D(Screen, caption))
         .def("setCaption", &Screen::setCaption, D(Screen, setCaption))
         .def("background", &Screen::background, D(Screen, background))

In theory, as long as there is some opaque binding from the library you are using, NanoGUI doesn't care. You just need to be able to get access to the "python pointer" to your window. Internally, as long as the python object they return refers to a valid GLFWWindow in memory, I don't think it matters what it's "python type" is. So

screen = nanogui.Screen()  # make sure to call the empty constructor
their_glfw_window = some_method_from_them()
screen.initialize(their_glfw_window, False)

It may not be too bad, assuming the other library you are using is setting up GLFW / creating the windows. However, if you are wanting to initialize GLFW yourself and create windows, there's a lot more that would need to be done...see the CPP external GLFW example...it ain't exactly pretty.

NanoGUI just binds the GLFW constants in python/constants_glfw.cpp so that users can override the keyboard callbacks etc. Though binding the other stuff for GLFW could be done by NanoGUI, it's a lot of work. It would basically mean creating python bindings for every API call in GLFW...

I don't really have time to investigate another library, but in theory if your other library is creating the window and setting up GLFW, then all you need is

  1. Do not call nanogui.init() (since they are initializing GLFW)
  2. Find some way to get a "pointer" to their GLFW Window, and call your_screen.initialize(...)

I hope that makes sense, I'm writing this running-out-the-door...but wanted to share the binding code with you in case that was where you were stuck.