How to create a new window with a certain dimensions (for example 40% of screen size) using Python API?

Functionality-wise similar to File > User Preferences — a new window shows up with User Preferences area type.


2 Answers 2


Currently in Blender 2.79 the only way to manage windows through Python API is by bpy.ops.screen.area_dupli() and bpy.ops.wm.window_duplicate() operators.

But they add new screens to bpy.data.screens, which are not easy to remove.

And there is no way to resize created windows.


In my particular case I can use window created by userpref_show operator, it's size is very close to what I need and it creates 'temp' screen which is deleted after window is closed, so I do not need to cleanup bpy.data.screens afterwards:

import bpy

# Call user prefs window

# Change area type
area = bpy.context.window_manager.windows[-1].screen.areas[0]
area.type = "TEXT_EDITOR"


With render.view_show operator it is possible to set the exact size of created window by modifying render settings:

import bpy

# Modify render settings
render = bpy.context.scene.render
render.resolution_x = 640
render.resolution_y = 480
render.resolution_percentage = 100

# Modify preferences (to guaranty new window)
prefs = bpy.context.preferences
prefs.view.render_display_type = "WINDOW"

# Call image editor window

# Change area type
area = bpy.context.window_manager.windows[-1].screen.areas[0]
area.type = "TEXT_EDITOR"

# Restore render settings and preferences
# render.resolution_x = original_value
# ...

# I also restore is_dirty tag which affects preferences autosave feature
# prefs.is_dirty = original_value
  • $\begingroup$ Interesting that it is just as easy to set up buttons in a panel to create editors as popups for specific uses - I made mine a small Image Editor and set it to Paint in the script, and this is actually useful. Thanks. $\endgroup$ Nov 2, 2017 at 20:52
  • $\begingroup$ simple hack but works as expected, I'm looking a way to create multiple of the window, currently this code only allow only one instance of window $\endgroup$
    – aditia
    Oct 30, 2018 at 5:41
  • $\begingroup$ ah by adding as follow allow me to add a lot of windows with different type python import bpy # Call user prefs window bpy.ops.screen.userpref_show('INVOKE_DEFAULT') # Change area type area = bpy.context.window_manager.windows[-1].screen.areas[0] area.type = 'TEXT_EDITOR' bpy.ops.wm.window_duplicate() area.type = 'CONSOLE' $\endgroup$
    – aditia
    Oct 30, 2018 at 5:45
  • $\begingroup$ @aditia I do not recommend this, each time you use window_duplicate operator it will add additional screen temp.001 that will not be removed after you close the second window, which will mess up your workflow if you switch between screens as often as I do. $\endgroup$ Oct 31, 2018 at 3:32
  • $\begingroup$ @MikhailRachinskiy ah I see, you're correct, using dupli make screen messier $\endgroup$
    – aditia
    Nov 1, 2018 at 4:30

I have been struggeling with this as well and would like to share my solution. I wanted to be able to open several windows and change their layout in my script. bpy.ops.screen.userpref_show() opens a window with just a single area but it wouldn't allow me to open several windows and the windows would have the title "Blender User Preferences". bpy.ops.wm.window_duplicate() would allow me to open several windows and make them the same size as the current window, but it would open with my multi-area screen and produce a new screen everytime without removing it. This is my solution:

A helper to create contexts for operator calls:

# Construct context
def context(window=None, screen=None, area=None, region=None):
    if window is None: window = C.window
    if screen is None: screen = window.screen
    if area is None: area = screen.areas[0]
    if region is None: region = area.regions[0]
    return {'window': window, 'screen': screen, 'area': area, 'region': region}

Wrappers for Window, Screen and Area:

# Window class wrapper
class Window:
    def __init__(self, window):
        self.window = window

    # Get window screen
    def get_screen(self):
        return Screen(self.window.screen)

    # Create new window
    def new(cls, screen_name):
        window = cls(C.window_manager.windows[-1])
        return window

# Screen class wrapper
class Screen:
    def __init__(self, screen):
        self.screen = screen

    # Get name
    def get_name(self, name):
        return self.screen.name

    # Set name
    def set_name(self, name):
        self.screen.name = name

        # Return self for method chaining
        return self

    # Get scene
    def get_scene(self):
        return self.screen.scene

    # Set scene
    def set_scene(self, scene):
        self.screen.scene = scene

        # Return self for method chaining
        return self

    # Get area
    def area_at(self, index):
        return Area(self.screen.areas[index])

    # Split area and return new one
    def split_area(self, area, dir, factor):
        # Set direction
        if 'vertical'.startswith(dir.lower()): dir = 'VERTICAL'
        elif 'horizontal'.startswith(dir.lower()): dir = 'HORIZONTAL'
        else: raise ValueError("Invalid direction \"%s\""%dir)

            context(screen=self.screen, area=area.area),
            direction=dir, factor=factor
        return Area(self.screen.areas[-1])

    # Join areas and return success status
    def join_areas(self, area1, area2):
        # Try joining areas
        result = bpy.ops.screen.area_join(context(screen=self.screen),
            min_x=area1.x, min_y=area1.y,
            max_x=area2.x, max_y=area2.y

        # Return eliminated area
        return ('FINISHED' in result)

    # Join all areas
    def join_all(self):
        # Create area index
        index = self.AreaIndex(self.screen.areas)

        # Join all areas
        for i in range(index.count):
            for key in index.x:
                for a1, a2 in index.x_pair_iterator(key):
                    if self.join_areas(a1, a2):
            for key in index.y:
                for a1, a2 in index.y_pair_iterator(key):
                    if self.join_areas(a1, a2):
            if index.count <= 1: break

        # Return self for method chaining
        return self

    # Area index for area joining
    class AreaIndex:
        # Create area index
        def __init__(self, areas):
            # Create index maps
            self.x = {}
            self.y = {}
            self.count = len(areas)

            # Insert areas into indices
            for area in areas:
                if not area.x in self.x: self.x[area.x] = []
                if not area.y in self.y: self.y[area.y] = []

            # Sort area lists
            for key in self.x: self.x[key].sort(key=lambda a: a.y)
            for key in self.y: self.y[key].sort(key=lambda a: a.x)

        # Remove area from index
        def remove_area(self, area):
            self.count -= 1

        # Iterate over area pairs for x key
        def x_pair_iterator(self, key):
            for i in range(len(self.x[key]) - 1):
                yield (self.x[key][i], self.x[key][i + 1])

        # Iterate over area pairs for x key
        def y_pair_iterator(self, key):
            for i in range(len(self.y[key]) - 1):
                yield (self.y[key][i], self.y[key][i + 1])

# Area class wrapper
class Area:
    def __init__(self, area):
        self.area = area

    # Get area type
    def get_type(self):
        return self.area.type

    # Set area type
    def set_type(self, type):
        self.area.type = type

        # Return self for method chaining
        return self

Create a window with two areas and remove unused screens:

SCREEN_NAME = "temp"

# Create new window
window = Window.new(SCREEN_NAME)
screen = window.get_screen().set_scene(D.scenes['Test'])

# Setup areas
node_area = screen.area_at(0).set_type('NODE_EDITOR')
movie_area = screen.split_area(node_area, 'v', 0.7).set_type('CLIP_EDITOR')

# Get used screens
used_screens = set(
    w.screen for w in C.window_manager.windows
    if w.screen.name.startswith(SCREEN_NAME)

# Delete unused screens
screen_backup = C.window.screen
for s in D.screens:
    if s.name.startswith(SCREEN_NAME) and s not in used_screens:
C.window.screen = screen_backup

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .