def load(): """ If docs fail to load, new ones are created and saved. """ prefs_file_path = userfolders.get_config_dir() + PREFS_DOC recents_file_path = userfolders.get_config_dir() + RECENT_DOC global prefs, recent_projects try: prefs = utils.unpickle(prefs_file_path) except: prefs = EditorPreferences() with atomicfile.AtomicFileWriter(prefs_file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(prefs, write_file) # Override deprecated preferences to default values. prefs.delta_overlay = True prefs.auto_play_in_clip_monitor = False prefs.empty_click_exits_trims = True prefs.quick_enter_trims = True prefs.remember_monitor_clip_frame = True try: recent_projects = utils.unpickle(recents_file_path) except: recent_projects = utils.EmptyClass() recent_projects.projects = [] with atomicfile.AtomicFileWriter(recents_file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(recent_projects, write_file) # Remove non-existing projects from recents list remove_list = [] for proj_path in recent_projects.projects: if os.path.isfile(proj_path) == False: remove_list.append(proj_path) if len(remove_list) > 0: for proj_path in remove_list: recent_projects.projects.remove(proj_path) with atomicfile.AtomicFileWriter(recents_file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(recent_projects, write_file) # Versions of program may have different prefs objects and # we may need to to update prefs on disk if user has e.g. # installed later version of Flowblade. current_prefs = EditorPreferences() if len(prefs.__dict__) != len(current_prefs.__dict__): current_prefs.__dict__.update(prefs.__dict__) prefs = current_prefs with atomicfile.AtomicFileWriter(prefs_file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(prefs, write_file) print("prefs updated to new version, new param count:", len(prefs.__dict__))
def save(): """ Write out prefs and recent_projects files """ prefs_file_path = userfolders.get_config_dir() + PREFS_DOC recents_file_path = userfolders.get_config_dir() + RECENT_DOC with atomicfile.AtomicFileWriter(prefs_file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(prefs, write_file) with atomicfile.AtomicFileWriter(recents_file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(recent_projects, write_file)
def _get_render_profile(project_profile, render_size, render_folder): new_width, new_height = _get_render_dimensions(project_profile, render_size) file_contents = "description=" + "proxy render profile" + "\n" file_contents += "frame_rate_num=" + str( project_profile.frame_rate_num()) + "\n" file_contents += "frame_rate_den=" + str( project_profile.frame_rate_den()) + "\n" file_contents += "width=" + str(new_width) + "\n" file_contents += "height=" + str(new_height) + "\n" file_contents += "progressive=1" + "\n" file_contents += "sample_aspect_num=" + str( project_profile.sample_aspect_num()) + "\n" file_contents += "sample_aspect_den=" + str( project_profile.sample_aspect_den()) + "\n" file_contents += "display_aspect_num=" + str( project_profile.display_aspect_num()) + "\n" file_contents += "display_aspect_den=" + str( project_profile.display_aspect_den()) + "\n" render_profile_path = render_folder + "/temp_render_profile" with atomicfile.AtomicFileWriter(render_profile_path, "w") as afw: profile_file = afw.get_file() profile_file.write(file_contents) render_profile = mlt.Profile(render_profile_path) return render_profile
def _get_proxy_profile(project): project_profile = project.profile new_width, new_height = _get_proxy_dimensions(project_profile, project.proxy_data.size) file_contents = "description=" + "proxy render profile" + "\n" file_contents += "frame_rate_num=" + str( project_profile.frame_rate_num()) + "\n" file_contents += "frame_rate_den=" + str( project_profile.frame_rate_den()) + "\n" file_contents += "width=" + str(new_width) + "\n" file_contents += "height=" + str(new_height) + "\n" file_contents += "progressive=1" + "\n" file_contents += "sample_aspect_num=" + str( project_profile.sample_aspect_num()) + "\n" file_contents += "sample_aspect_den=" + str( project_profile.sample_aspect_den()) + "\n" file_contents += "display_aspect_num=" + str( project_profile.display_aspect_num()) + "\n" file_contents += "display_aspect_den=" + str( project_profile.display_aspect_den()) + "\n" proxy_profile_path = userfolders.get_cache_dir() + "temp_proxy_profile" with atomicfile.AtomicFileWriter(proxy_profile_path, "w") as afw: profile_file = afw.get_file() profile_file.write(file_contents) proxy_profile = mlt.Profile(proxy_profile_path) return proxy_profile
def save_current_colors(): # Used to communicate theme colors to tools like gmic.py running on separate process colors = (unpack_gdk_color(_selected_bg_color), unpack_gdk_color(_bg_color), unpack_gdk_color(_button_colors)) save_file_path = _colors_data_path() with atomicfile.AtomicFileWriter(save_file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(colors, write_file)
def _edl_xml_render_done(data): edl_path = data mlt_parse = MLTXMLToEDLParse(get_edl_temp_xml_path(), current_sequence()) edl_contents = mlt_parse.create_edl() with atomicfile.AtomicFileWriter(edl_path, "w") as afw: f = afw.get_file() f.write(edl_contents)
def write_status_message(msg): try: status_msg_file = session_folder() + "/" + STATUS_MSG_FILE with atomicfile.AtomicFileWriter(status_msg_file, "w") as afw: script_file = afw.get_file() script_file.write(msg) except: pass # this failing because we can't get file access will show as progress hickup to user, we don't care
def save(self, save_file_path): save_data = copy.copy(self) for layer in save_data.layers: layer.pango_layout = None with atomicfile.AtomicFileWriter(save_file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(save_data, write_file) self.create_pango_layouts() # we just destroyed these because they don't pickle, they need to be recreated.
def _save_animation_dialog_callback(dialog, response_id): if response_id == Gtk.ResponseType.ACCEPT: file_path = dialog.get_filenames()[0] # Write out file. with atomicfile.AtomicFileWriter(file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(_animation_instance, write_file) dialog.destroy() else: dialog.destroy()
def write_misc_session_data(session_id, file_name, misc_data): folder = _get_session_folder(session_id) data_path = folder + "/" + file_name if os.path.exists(data_path): os.remove(data_path) with atomicfile.AtomicFileWriter(data_path, "wb") as afw: outfile = afw.get_file() pickle.dump(misc_data, outfile)
def set_render_data(session_id, video_render_data): folder = _get_session_folder(session_id) render_data_path = folder + "/" + RENDER_DATA_FILE if os.path.exists(render_data_path): os.remove(render_data_path) with atomicfile.AtomicFileWriter(render_data_path, "wb") as afw: outfile = afw.get_file() pickle.dump(video_render_data, outfile)
def abort_render(session_id): folder = _get_session_folder(session_id) abort_msg_file = folder + "/" + ABORT_MSG_FILE try: with atomicfile.AtomicFileWriter(abort_msg_file, "wb") as afw: outfile = afw.get_file() pickle.dump("##abort", outfile) except atomicfile.AtomicFileWriteError: # Sometimes this fails and not handling it makes things worse, see if this needs more attention. print("atomicfile.AtomicFileWriteError in ccrutils.abort_render(), could not open for writing: ", folder)
def _save_opts_dialog_callback(dialog, response_id): if response_id == Gtk.ResponseType.ACCEPT: file_path = dialog.get_filenames()[0] buf = widgets.args_panel.opts_view.get_buffer() opts_text = buf.get_text(buf.get_start_iter(), buf.get_end_iter(), include_hidden_chars=True) with atomicfile.AtomicFileWriter(file_path, "w") as afw: opts_file = afw.get_file() opts_file.write(opts_text) dialog.destroy() else: dialog.destroy()
def run(self): self.start_time = time.monotonic() self.render_player = None self.frames_range_writer = None self.abort = False self.script_renderer = None frame_name = "frame" profile = mltprofiles.get_profile(self.profile_desc) # Delete old preview frames for frame_file in os.listdir(_clip_frames_folder): file_path = os.path.join(_clip_frames_folder, frame_file) os.remove(file_path) self.frames_range_writer = gmicplayer.FramesRangeWriter( self.clip_path, self.frames_update, profile) self.frames_range_writer.write_frames(_clip_frames_folder + "/", frame_name, self.range_in, self.range_out) if self.abort == True: return script_file = open(self.script_path) user_script = script_file.read() print(user_script) while len(os.listdir(_clip_frames_folder)) != self.length: print("WAITING") time.sleep(0.5) # Render frames with gmic script self.script_renderer = gmicplayer.FolderFramesScriptRenderer( user_script, _clip_frames_folder, _rendered_frames_folder + "/", frame_name, self.script_render_update_callback, self.script_render_output_callback, nice=10, re_render_existing=False) self.script_renderer.write_frames() # Write out completed flag file. completed_msg_file = _session_folder + "/" + COMPLETED_MSG_FILE script_text = "##completed##" # let's put something in here with atomicfile.AtomicFileWriter(completed_msg_file, "w") as afw: script_file = afw.get_file() script_file.write(script_text)
def remove_non_existing_recent_projects(): # Remove non-existing projects from recents list recents_file_path = userfolders.get_config_dir() + RECENT_DOC remove_list = [] for proj_path in recent_projects.projects: if os.path.isfile(proj_path) == False: remove_list.append(proj_path) if len(remove_list) > 0: for proj_path in remove_list: recent_projects.projects.remove(proj_path) with atomicfile.AtomicFileWriter(recents_file_path, "wb") as afw: write_file = afw.get_file() pickle.dump(recent_projects, write_file)
def run(self): frame_levels = [None] * self.clip_media_length for frame in range(0, len(frame_levels)): self.temp_clip.seek(frame) mlt.frame_get_waveform(self.temp_clip.get_frame(), 10, 50) val = self.levels.get(RIGHT_CHANNEL) if val == None: val = 0.0 frame_levels[frame] = float(val) self.last_rendered_frame = frame with atomicfile.AtomicFileWriter(self.file_cache_path, "wb") as afw: write_file = afw.get_file() pickle.dump(frame_levels, write_file)
def _save_changed_xml_file(s_media_file, new_profile): xml_file = open(s_media_file.path) xml_text = xml_file.read() new_profile_node = mltprofiles.get_profile_node(new_profile) in_index = xml_text.find("<profile") out_index = xml_text.find("/>", in_index) + 2 new_xml_text = xml_text[0:in_index] + new_profile_node + xml_text[out_index:len(xml_text)] folder = editorpersistance.prefs.render_folder uuid_str = md5.new(str(os.urandom(32))).hexdigest() new_xml_file_path = folder + "/"+ uuid_str + ".xml" with atomicfile.AtomicFileWriter(new_xml_file_path, "w") as afw: new_xml_file = afw.get_file() new_xml_file.write(new_xml_text) return new_xml_file_path
def _save_changed_xml_file(s_media_file, new_profile): xml_file = open(s_media_file.path) xml_text = xml_file.read() new_profile_node = mltprofiles.get_profile_node(new_profile) in_index = xml_text.find("<profile") out_index = xml_text.find("/>", in_index) + 2 new_xml_text = xml_text[0:in_index] + new_profile_node + xml_text[out_index:len(xml_text)] folder = userfolders.get_render_dir() uuid_str = hashlib.md5(str(os.urandom(32)).encode('utf-8')).hexdigest() new_xml_file_path = folder + "/"+ uuid_str + ".xml" with atomicfile.AtomicFileWriter(new_xml_file_path, "w") as afw: new_xml_file = afw.get_file() new_xml_file.write(new_xml_text) return new_xml_file_path
def _save_profile_clicked(widgets, user_profiles_view): load_profile_combo, description, f_rate_num, f_rate_dem, width, height, \ s_rate_num, s_rate_dem, d_rate_num, d_rate_dem, progressive = widgets profile_file_name = description.get_text().lower().replace(os.sep, "_").replace( " ", "_") file_contents = "description=" + description.get_text() + "\n" file_contents += "frame_rate_num=" + f_rate_num.get_text() + "\n" file_contents += "frame_rate_den=" + f_rate_dem.get_text() + "\n" file_contents += "width=" + width.get_text() + "\n" file_contents += "height=" + height.get_text() + "\n" if progressive.get_active() == True: prog_val = "1" else: prog_val = "0" file_contents += "progressive=" + prog_val + "\n" file_contents += "sample_aspect_num=" + s_rate_num.get_text() + "\n" file_contents += "sample_aspect_den=" + s_rate_dem.get_text() + "\n" file_contents += "display_aspect_num=" + d_rate_num.get_text() + "\n" file_contents += "display_aspect_den=" + d_rate_dem.get_text() + "\n" profile_path = userfolders.get_data_dir( ) + mltprofiles.USER_PROFILES_DIR + profile_file_name if os.path.exists(profile_path): dialogutils.warning_message(_("Profile '") + description.get_text() + _("' already exists!"), \ _("Delete profile and save again."), gui.editor_window.window) return with atomicfile.AtomicFileWriter(profile_path, "w") as afw: profile_file = afw.get_file() profile_file.write(file_contents) dialogutils.info_message(_("Profile '") + description.get_text() + _("' saved."), \ _("You can now create a new project using the new profile."), gui.editor_window.window) mltprofiles.load_profile_list() render.reload_profiles() user_profiles_view.fill_data_model(mltprofiles.get_user_profiles())
def run(self): Gdk.threads_enter() _info_window.info.set_text("Loading project " + self.filename + "...") Gdk.threads_leave() persistance.show_messages = False target_project = persistance.load_project(self.filename, False, True) target_project.c_seq = target_project.sequences[ target_project.c_seq_index] # Media file media assets media_assets = "" for media_file_id, media_file in target_project.media_files.items(): if isinstance(media_file, patternproducer.AbstractBinClip): continue if os.path.isfile(media_file.path): media_assets = media_assets + str(media_file.path) + "\n" with atomicfile.AtomicFileWriter(_get_assets_file(), "w") as afw: f = afw.get_file() f.write(media_assets) _shutdown()
def save(self, save_path): with atomicfile.AtomicFileWriter(save_path, "wb") as afw: write_file = afw.get_file() pickle.dump(self, write_file)
def abort_render(session_id): folder = _get_session_folder(session_id) abort_msg_file = folder + "/" + ABORT_MSG_FILE with atomicfile.AtomicFileWriter(abort_msg_file, "wb") as afw: outfile = afw.get_file() pickle.dump("##abort", outfile)
def _launch_render(self, clip, range_in, range_out, clip_start_offset): self.create_data_dirs_if_needed() job_msg = self.get_job_queue_message() job_msg.text = _("Render Starting...") job_msg.status = jobs.RENDERING jobs.update_job_queue(job_msg) self.render_range_in = range_in self.render_range_out = range_out self.clip_start_offset = clip_start_offset blenderheadless.clear_flag_files(self.get_container_program_id()) # We need data to be available for render process, # create video_render_data object with default values if not available. if self.container_data.render_data == None: self.container_data.render_data = toolsencoding.create_container_clip_default_render_data_object( current_sequence().profile) # Make sure preview render does not try to create video clip. render_data = copy.deepcopy(self.container_data.render_data) if self.render_type == PREVIEW_RENDER: render_data.do_video_render = False render_data.is_preview_render = True blenderheadless.set_render_data(self.get_container_program_id(), render_data) # Write current render target container clip for blenderrendersetup.py cont_info_id_path = userfolders.get_cache_dir( ) + "blender_render_container_id" with atomicfile.AtomicFileWriter(cont_info_id_path, "w") as afw: outfile = afw.get_file() outfile.write(str(self.get_container_program_id())) # Write current render set up exec lines for blenderrendersetup.py if self.render_type != PREVIEW_RENDER: file_path_str = 'bpy.context.scene.render.filepath = "' + self.get_rendered_media_dir( ) + '/frame"' + NEWLINE else: file_path_str = 'bpy.context.scene.render.filepath = "' + self.get_preview_media_dir( ) + '/frame"' + NEWLINE if not os.path.exists(self.get_preview_media_dir()): os.mkdir(self.get_preview_media_dir()) render_exec_lines = file_path_str \ + 'bpy.context.scene.render.fps = 24' + NEWLINE \ + 'bpy.context.scene.render.image_settings.file_format = "PNG"' + NEWLINE \ + 'bpy.context.scene.render.image_settings.color_mode = "RGBA"' + NEWLINE \ + 'bpy.context.scene.render.film_transparent = 1' + NEWLINE \ + 'bpy.context.scene.render.resolution_x = '+ str(current_sequence().profile.width()) + NEWLINE \ + 'bpy.context.scene.render.resolution_y = ' + str(current_sequence().profile.height()) + NEWLINE \ + 'bpy.context.scene.render.resolution_percentage = 100' + NEWLINE \ + 'bpy.context.scene.frame_start = ' + str(range_in) + NEWLINE \ + 'bpy.context.scene.frame_end = ' + str(range_out) + NEWLINE render_exec_lines = self._write_exec_lines_for_obj_type( render_exec_lines, "objects") render_exec_lines = self._write_exec_lines_for_obj_type( render_exec_lines, "materials") render_exec_lines = self._write_exec_lines_for_obj_type( render_exec_lines, "curves") exec_lines_file_path = self.get_session_dir( ) + "/blender_render_exec_lines" with atomicfile.AtomicFileWriter(exec_lines_file_path, "w") as afw: outfile = afw.get_file() outfile.write(render_exec_lines) args = ( "session_id:" + self.get_container_program_id(), "project_path:" + utils.escape_shell_path( self.container_data.program ), #.replace(" ", "\ "), # This is going through Popen shell=True and needs escaped spaces. "range_in:" + str(range_in), "range_out:" + str(range_out), "profile_desc:" + PROJECT().profile.description().replace(" ", "_")) # Run with nice to lower priority if requested (currently hard coded to lower) nice_command = "nice -n " + str(10) + " " + str( respaths.LAUNCH_DIR + "flowbladeblenderheadless").replace( " ", "\ ") for arg in args: nice_command += " " nice_command += arg subprocess.Popen([nice_command], shell=True)
def save_project(project, file_path, changed_profile_desc=None): """ Creates pickleable project object """ print("Saving project...") # + os.path.basename(file_path) # Get shallow copy s_proj = copy.copy(project) # Implements "change profile" functionality global _fps_conv_mult, _xml_new_paths_for_profile_change _fps_conv_mult = 1.0 if changed_profile_desc != None: _fps_conv_mult = mltprofiles.get_profile(changed_profile_desc).fps( ) / mltprofiles.get_profile(s_proj.profile_desc).fps() s_proj.profile_desc = changed_profile_desc _xml_new_paths_for_profile_change = { } # dict acts also as a flag to show that profile change save is happening new_profile = mltprofiles.get_profile(changed_profile_desc) #print "Saving changed profile project: ", changed_profile_desc #print "FPS conversion multiplier:", _fps_conv_mult else: _xml_new_paths_for_profile_change = None # None value acts also as a flag to show that profile change save is _not_ happening # Set current sequence index s_proj.c_seq_index = project.sequences.index(project.c_seq) # Set project SAVEFILE_VERSION to current in case this is a resave of older file type. # Older file type has been converted to newer file type on load. s_proj.SAVEFILE_VERSION = appconsts.SAVEFILE_VERSION # Init proxy convert data global project_proxy_mode, proxy_path_dict project_proxy_mode = s_proj.proxy_data.proxy_mode proxy_path_dict = {} # Replace media file objects with pickleable copys media_files = {} for k, v in s_proj.media_files.items(): s_media_file = copy.copy(v) # Because of MLT misfeature of changing project profile when loading MLT XML files we need to create new modified XML files when # saving to change profile. # Underlying reason: https://github.com/mltframework/mlt/issues/212 if changed_profile_desc != None and hasattr( s_media_file, "path" ) and s_media_file.path != None and utils.is_mlt_xml_file( s_media_file.path) == True: new_xml_file_path = _save_changed_xml_file(s_media_file, new_profile) _xml_new_paths_for_profile_change[ s_media_file.path] = new_xml_file_path s_media_file.path = new_xml_file_path #print "XML path replace for media:", s_media_file.path, new_xml_file_path # Remove unpicleable attrs remove_attrs(s_media_file, MEDIA_FILE_REMOVE) # Convert media files between original and proxy files if project_proxy_mode == appconsts.CONVERTING_TO_USE_PROXY_MEDIA: if s_media_file.has_proxy_file: proxy_path_dict[ s_media_file.path] = s_media_file.second_file_path s_media_file.set_as_proxy_media_file() elif project_proxy_mode == appconsts.CONVERTING_TO_USE_ORIGINAL_MEDIA: if s_media_file.is_proxy_file: proxy_path_dict[ s_media_file.path] = s_media_file.second_file_path s_media_file.set_as_original_media_file() # Change paths when doing snapshot save. Image sequences are not # md5 hashed and are saved in folders and need to be looked up by relative search # when loading. if snapshot_paths != None: if s_media_file.type != appconsts.PATTERN_PRODUCER and s_media_file.type != appconsts.IMAGE_SEQUENCE: s_media_file.path = snapshot_paths[s_media_file.path] media_files[s_media_file.id] = s_media_file s_proj.media_files = media_files # Replace sequences with pickleable objects sequences = [] for i in range(0, len(project.sequences)): add_seq = project.sequences[i] sequences.append(get_p_sequence(add_seq)) s_proj.sequences = sequences # Remove unpickleable attributes remove_attrs(s_proj, PROJECT_REMOVE) # Write out file. with atomicfile.AtomicFileWriter(file_path, "wb") as afw: outfile = afw.get_file() pickle.dump(s_proj, outfile)
def write_completed_message(): completed_msg_file = session_folder() + "/" + COMPLETED_MSG_FILE script_text = "##completed##" # let's put something in here with atomicfile.AtomicFileWriter(completed_msg_file, "w") as afw: script_file = afw.get_file() script_file.write(script_text)
def _launch_render(self, clip, range_in, range_out, clip_start_offset): self.create_data_dirs_if_needed() self.render_range_in = range_in self.render_range_out = range_out self.clip_start_offset = clip_start_offset blenderheadless.clear_flag_files(self.get_container_program_id()) # We need data to be available for render process, # create video_render_data object with default values if not available. if self.container_data.render_data == None: self.container_data.render_data = toolsencoding.create_container_clip_default_render_data_object( current_sequence().profile) blenderheadless.set_render_data(self.get_container_program_id(), self.container_data.render_data) # Write current render target container clip for blenderrendersetup.py cont_info_id_path = userfolders.get_cache_dir( ) + "blender_render_container_id" with atomicfile.AtomicFileWriter(cont_info_id_path, "w") as afw: outfile = afw.get_file() outfile.write(str(self.get_container_program_id())) # Write current render set up exec lines for blenderrendersetup.py render_exec_lines = 'bpy.context.scene.render.filepath = "' + self.get_rendered_media_dir() + '/frame"' + NEWLINE \ + 'bpy.context.scene.render.fps = 24' + NEWLINE \ + 'bpy.context.scene.render.image_settings.file_format = "PNG"' + NEWLINE \ + 'bpy.context.scene.render.image_settings.color_mode = "RGBA"' + NEWLINE \ + 'bpy.context.scene.render.film_transparent = 1' + NEWLINE \ + 'bpy.context.scene.render.resolution_x = '+ str(current_sequence().profile.width()) + NEWLINE \ + 'bpy.context.scene.render.resolution_y = ' + str(current_sequence().profile.height()) + NEWLINE \ + 'bpy.context.scene.render.resolution_percentage = 100' + NEWLINE \ + 'bpy.context.scene.frame_start = ' + str(range_in) + NEWLINE \ + 'bpy.context.scene.frame_end = ' + str(range_out) + NEWLINE objects = self.blender_objects() for obj in objects: # objects are [name, type, editorlist], see blenderprojectinit.py obj_name = obj[0] editor_lines = [] for editor_data in obj[2]: # editor_data is [prop_path, label, tooltip, editor_type, value], see containerprogramedit.EditorManagerWindow.get_current_editor_data() prop_path = editor_data[0] value = editor_data[4] render_exec_lines += 'obj = bpy.data.objects["' + obj_name + '"]' + NEWLINE render_exec_lines += 'obj.' + prop_path + " = " + value + NEWLINE exec_lines_file_path = self.get_session_dir( ) + "/blender_render_exec_lines" with atomicfile.AtomicFileWriter(exec_lines_file_path, "w") as afw: outfile = afw.get_file() outfile.write(render_exec_lines) args = ("session_id:" + self.get_container_program_id(), "project_path:" + self.container_data.program, "range_in:" + str(range_in), "range_out:" + str(range_out), "profile_desc:" + PROJECT().profile.description().replace(" ", "_")) # Run with nice to lower priority if requested (currently hard coded to lower) nice_command = "nice -n " + str( 10) + " " + respaths.LAUNCH_DIR + "flowbladeblenderheadless" for arg in args: nice_command += " " nice_command += arg subprocess.Popen([nice_command], shell=True)