Search code examples
pythonpyglettrimesh

How can I center a trimesh window on screen?


I have started to work with Python's trimesh package and the first thing I want to do is to set the size of the graphics window and its position on the screen.

I figured out how to resize the graphics window, as the simple code below (from https://github.com/mikedh/trimesh/blob/main/examples/colors.ipynb) shows:

import trimesh

# The XAML file is found at https://github.com/mikedh/trimesh/blob/main/models/machinist.XAML
mesh = trimesh.load("machinist.XAML", process=False)
mesh.visual.kind
# Resolution is an undocumated argument - I had to figure it out myself
mesh.show(resolution=(600,600))

But I also want to center the mesh window on screen. Does anyone know how?


Solution

  • After looking through the classes and methods for a quite a while, I finally found a solution.

    The show method in the Scene class accepts a SceneViewer class as its viewer parameter (which is the default viewer, unless in a notebook)

    The SceneViewer itself inherits from the pyglet.window.Window, which does have a set_location method. So, I ended up creating a custom class CenteredSceneViewer that centers itself by calculating the x and y coordinates of the centered window and invoking the set_location:

    import trimesh
    from trimesh.viewer.windowed import SceneViewer
    from pyglet.canvas import Display
    
    # A SceneViewer that will center itself on the screen
    class CenteredSceneViewer(SceneViewer):
        def __init__(self, *args, **kwargs):
            # Get the screen resolution
            self.resolution = kwargs.get("resolution", (600, 600))
            display = Display()
            screen = display.get_default_screen()
            self.screen_width = screen.width
            self.screen_height = screen.height
    
            # We only need to center once
            self.centered = False
    
            super(CenteredSceneViewer, self).__init__(*args, **kwargs)
    
        def dispatch_event(self, *args):
            if not self.centered:
                # Calculate the position to center the window
                window_x = (self.screen_width - self.resolution[0]) // 2
                window_y = (self.screen_height - self.resolution[1]) // 2
    
                # Set the location of the window
                self.set_location(window_x, window_y)
                self.centered = True
    
            super(CenteredSceneViewer, self).dispatch_event(*args)
    
    
    mesh = trimesh.load("machinist.XAML", process=False)
    mesh.show(viewer=CenteredSceneViewer, resolution=(600, 600))