2

I have an existing open window, created by SDL 2 in C/C++. I would like the process to call a python script (using Boost/Python) to add some GUI elements to it.

Here's a non-working example:

import sys
import sdl2
import sdl2.ext

import tkinter as tk
from tkinter import *

# Initialize SDL2
sdl2.ext.init()

# Set up the window
window_width = 800
window_height = 600
window_title = "SDL2 Window"
window = sdl2.ext.Window(window_title, size=(window_width, window_height))

window.show()

# Get SDL handle for the window
wminfo = sdl2.SDL_SysWMinfo()
sdl2.SDL_VERSION(wminfo.version)


window_id = sdl2.SDL_GetWindowWMInfo(window.window, wminfo)
print(wminfo.info.win.window)


#tkinter
The tkinter code examples mentioned below come here

Looking at https://docs.python.org/3/library/tkinter.html#tkinter.Tk, I see that

use
Specifies the id of the window in which to embed the application, instead of it being created as an independent toplevel window.

However, it also adds

Note that on some platforms this will only work correctly if id refers to a Tk frame or toplevel that has its -container option enabled.

Running the following prints a window id similar to window_id above.

root = tk.Tk()
tk_id = root.winfo_id()

Running child = tk.Tk(use=str(wminfo.info.win.window)) causes python to exit without an exception. VS Code writes Server[1] disconnected unexpectedly but I don't think this is the X11 server but rather the debugger.

Running root = tk.Tk() child = tk.Tk(use=str(tk_id)) causes python to exit as well.

Can this be done at all? Am I missing something simple here?

7
  • 1
    It is not clear what you want. What is "call a python script"? Using import or subprocess? What kind of GUI elements? tkinter or SDL? Also you should provide a minimal reproducible example and elaborate more clearly what your issue is. Commented May 28 at 1:10
  • No, you can't embed Tkinter elements into an arbitrary SDL2 window. You can try hacks, but you will almost certainly end up with a crash or unstable application. Commented May 28 at 13:34
  • If your aim was to have c/c++ call some python script to get dynamic data and add it as element then you can just get the data inside c/c++ and use normal sdl2 elements to show it. Instead if you fantasy some tkinter widget then i dont think you can do it. Commented May 31 at 12:29
  • Why don't you ask c/cpp to create python tkinter window and in it add the sdl window and tkinter widget if you expect something like a tkinter button overlay, then you will have to split sdl window into 4 parts and place them around button.. Another option though a bit unresponsive is to reder both tkinter and sdl window(you may want to hide title of tkinter) and sync the position of both windows Commented May 31 at 12:34
  • Ok i got to put tkinter inside sdl using ctypes linking but just have one issue which is unresolvable tkinter creates a window always and so the tkinter window will be visible(but empty as it's contents were rendered inside sdl window by hwnd reference) along with sdl window. Commented May 31 at 13:44

2 Answers 2

1
+50

As you said I am adding my work over this though this is not complete answer and it is heavily buggy.

first have a empty root for this test: inside it have:

app.cpp

#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h>
#include <iostream>
#include <fstream>

int main() {
    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window* window = SDL_CreateWindow("SDL2 Main Window",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);

    SDL_SysWMinfo info;
    SDL_VERSION(&info.version);
    SDL_GetWindowWMInfo(window, &info);
    HWND hwnd = info.info.win.window;

    std::ofstream hwnd_out("hwnd.txt");
    hwnd_out << (uintptr_t)hwnd;
    hwnd_out.close();

    SDL_Event e;
    bool running = true;
    while (running) {
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT)
                running = false;
        }
        SDL_Delay(16);
    }

    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

code.py

# tk_embed.py
import tkinter as tk
import ctypes
import time
import random

# Read HWND of SDL2 window
with open("hwnd.txt", "r") as f:
    hwnd_sdl = int(f.read().strip())

# Create Tkinter window
root = tk.Tk()
root.title("Tk Widget Inside SDL2")
root.geometry("300x200")
root.update_idletasks()
root.update()

hwnd_tk = root.winfo_id()

# Win32 API call
SetParent = ctypes.windll.user32.SetParent
SetWindowLong = ctypes.windll.user32.SetWindowLongW
GWL_STYLE = -16
WS_CHILD = 0x40000000
WS_VISIBLE = 0x10000000

# Reparent
SetParent(hwnd_tk, hwnd_sdl)
style = ctypes.windll.user32.GetWindowLongW(hwnd_tk, GWL_STYLE)
ctypes.windll.user32.SetWindowLongW(hwnd_tk, GWL_STYLE, style | WS_CHILD | WS_VISIBLE)

# Optionally reposition inside SDL2
MoveWindow = ctypes.windll.user32.MoveWindow
MoveWindow(hwnd_tk, 50, 50, 300, 200, True)

# Done

def random_color():
    # Generate a random color in hex format
    return "#{:06x}".format(random.randint(0, 0xFFFFFF))

def change_button_color():
    btn.config(bg=random_color())

tk.Label(root, text="This is embedded!").pack()
btn = tk.Button(root, text="Click me!", command=change_button_color)
btn.pack(pady=10)
root.mainloop()

how to run? compile the c++ using

g++ app.cpp -IC:/dev/SDL2-2.32.6/x86_64-w64-mingw32/include ^
  -LC:/dev/SDL2-2.32.6/x86_64-w64-mingw32/lib ^
  -lmingw32 -lSDL2main -lSDL2 -mwindows -o sdl_app.exe

(i have sdl in C:/dev/SDL2-2.32.6)

Now you will have sdl_app.exe in the root. Running this would start a empty sdl window and create a text file hwnd.txt containing the window's handle for python to use.

So now in cmd navigate to root and run the below

sdl_app.exe
python code.py
echo Done

As i said in comment: "i got to put tkinter inside sdl using ctypes linking but just have one issue which is unresolvable tkinter creates a window always and so the tkinter window will be visible(but empty as it's contents were rendered inside sdl window by hwnd reference) along with sdl window."

even we can make the additional tkinter window frameless. but hiding it is not possible. Also clicking on the tkinter window moves it to top left corner inside sdlwindow

The working of this is dirty low level hack that switches parent, and with this if you have window handle of legit application you can even put some hacky stuff in legit application's window..

Sign up to request clarification or add additional context in comments.

Comments

1

You can try these options:

Option 1: Run Tkinter in a separate window, coordinated with SDL

Instead of embedding Tk into SDL, create a separate Tk window, and pass data/events between SDL and Python:

## gui.py

import tkinter as tk
import threading

def run_tk_gui():
    root = tk.Tk()
    root.title("Control Panel")
    tk.Button(root, text="Click me!", command=lambda: print("Button clicked")).pack()
    root.mainloop()

# Start GUI in its own thread
threading.Thread(target=run_tk_gui, daemon=True).start()

# SDL loop can run in main thread

From your C++ app, you can invoke this script with Boost.Python, and the GUI will live alongside the SDL window — separately but in sync.

Ex : C++ side (using Boost.Python):

#include <boost/python.hpp>

void launchPythonGUI() {
    Py_Initialize();

    try {
        boost::python::object main_module = boost::python::import("__main__");
        boost::python::object main_namespace = main_module.attr("__dict__");

        // Import and run Python file
        boost::python::exec_file("gui.py", main_namespace);
    } catch (boost::python::error_already_set const&) {
        PyErr_Print();
    }
}

Option 2: Use SDL GUI libraries instead of Tkinter

Since you're already in SDL2, it would be much more stable to use a GUI framework that integrates with SDL, like:

  • Dear ImGui: Fast, immediate mode GUI library with SDL2 backends.

  • libRocket, Nuklear, or other GUI libraries designed to work with SDL/OpenGL.

You can call these from C++, and if needed, expose basic controls to Python via Boost.Python.

1 Comment

py ImGui is probably the way to go. I'll keep the question unanswered for the brave soul who can pull this off and answer my original ask.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.