# How to change render samples using python

I'm trying to render an image with python, and I want to set the render samples precisely, however, when I run this code:

bpy.context.scene.cycles.samples = 20


the number updates in the viewport but when an image is rendered, the number of samples it uses is unchanged. I'm really not sure what happening here...

• if you execute your line and ops.render.render() in the console, it works fine. try adding bpy.context.scene.update_tag() before the render command. Aug 7, 2020 at 11:43
• @FrederikSteinmetz hmm, I just tried that and it doesn't seem to be working, and now even if I edit it manually nothings happening Aug 7, 2020 at 11:49

Sometimes Blender just doesn't update things when the code is executed "blindly", so you need to imitate like some parts of it were called by user. To do so you need to create and register Blender Invoke or Modal Operator and then call it with ('INVOKE_DEFAULT'). In the most cases it solves such problems. I've written and tested this Modal Operator, it works. Maybe there's even no need in the whole modal section and you could place context.scene.cycles.samples = self.samples and bpy.ops.render.render('INVOKE_DEFAULT') right inside invoke method and just return {'FINISHED'} from there, but I just wanted to ensure that the samples have been changed using the modal loop. More information about Blender Operators you may find in the API documentation And here is the code with lots of comments:

import bpy
from bpy.props import IntProperty

class SetSamples(bpy.types.Operator):
'''Blender Modal Operator'''
bl_idname = 'render.set_cycles_samples'
bl_label = 'Set Cycles Samples'
samples : IntProperty(
name="samples",
min=0,
default=20
)
timer = None

def execute(self, context):
# in execute method we just create a timer and add a modal handler
# which handles events in real-time. We set the timer to the class
# variable to be able to remove it later on.
self.timer = bpy.context.window_manager.event_timer_add(.1, window=context.window)
return {'RUNNING_MODAL'} # this starts the modal() method

def modal(self, context, event):
# modal handles events in real-time
if event.type == 'ESC':
# in case something goes wrong and we need to abort it manually
bpy.context.window_manager.event_timer_remove(self.timer) # remove timer
return {'CANCELLED'}
elif event.type == 'TIMER':
print('Tick!') # just a test to count how many times timer ticks
# on every Timer tick the Operator checks:
if context.scene.cycles.samples == self.samples:
# if everything worked correctly and Cycles samples have changed:
bpy.context.window_manager.event_timer_remove(self.timer) # remove timer
bpy.ops.render.render('INVOKE_DEFAULT') # render
print('FINISHED') # just a test message after
return {'FINISHED'}
else:
# if samples haven't changed:
context.scene.cycles.samples = self.samples # command to change samples
context.scene.update_tag() # not sure what it does but it was recommended
# in the comments to the question
print(f'Cycles samples after changing: {context.scene.cycles.samples}') # test message
return {'PASS_THROUGH'} # returns us to the start of the modal() method

def invoke(self, context, event):
# Operator starts to execute from here when called with ('INVOKE_DEFAULT'),
# like if it was called by user, not by script
return self.execute(context) # this starts the execute() method

def register():
# as we use the Blender Operator class, it needs to be registered:
bpy.utils.register_class(SetSamples)

def unregister():
bpy.utils.unregister_class(SetSamples)

if __name__ == "__main__":
register()
# after register you can use your Operator just like any other Operator in Blender
# the last parameter is the samples you need
bpy.ops.render.set_cycles_samples('INVOKE_DEFAULT', samples = 20)


I managed to get it working just by wrapping all my code in between

class SimpleOperator(bpy.types.Operator):
#
code
#
bpy.utils.register_class(SimpleOperator)