How do I initiate a render from Python but still show progress bar and UI updates? When I run bpy.ops.render.render(write_still=True) from Python on large files, Blender goes into "(Not responding)" mode and the CPU spins like crazy for a few minutes. During this time, most users are confused as to what is going on in the application.

When you render manually from the GUI, it shows progress updates and gives the user a generally comfortable feeling that stuff is going on under the hood.

Is there any way to render from Python without freezing up the UI?
You can do this by passing the 'execution context'. By default the execute() function is run when calling an operator from a script. If you press the render button from within the UI the invoke() function is run. To do the same from a script you would pass 'INVOKE_DEFAULT' as execution context. So for example bpy.ops.render.render('INVOKE_DEFAULT', animation=False, write_still=True).

See also the Python API docs: https://docs.blender.org/api/blender_python_api_current/bpy.ops.html#execution-context

Or for 2.80 (which is basically the same at the time of writing): https://docs.blender.org/api/blender2.8/bpy.ops.html#execution-context