def createDir(path): """ Create a directory with the specified path. """ path = os.path.normpath(path) if os.path.isdir(path): verbose.print_("Directory already exists: %s" % path) pass else: try: os.makedirs(path) # Hide the folder if its name starts with a dot, as these files # are not automatically hidden on Windows if os.environ['IC_RUNNING_OS'] == "Windows": if os.path.basename(path).startswith('.'): setHidden(path) verbose.print_( 'mkdir "%s"' % path) # This causes an error if user config dir doesn't exist return path except: verbose.error("Cannot create directory: %s" % path) return False
def save(self): """ Save data. """ if self.j.save(): verbose.message("Job database saved.") return True else: verbose.error("Job database could not be saved.") return False
def saveAppPaths(self): """ Save the application paths to the data file. """ self.storeAppPathOSX() self.storeAppPathLinux() self.storeAppPathWin() if self.ap.save(): verbose.message("Application paths data file saved.") return True else: verbose.error("Application paths data file could not be saved.") return False
def openFile(self): """ Dialog accept function. """ try: for item in self.ui.fileBrowser_treeWidget.selectedItems(): filename = item.text(4) except ValueError: verbose.error("Nothing selected.") return False if self.session.file_open(filename): self.returnValue = filename self.accept()
def createShots(self): """ Create the shot(s). """ success = 0 existing = 0 failure = 0 shots_created = "" shots_existing = "" shots_failed = "" dialog_msg = "" for shot in self.shots_to_create: shot_datafile = self.getShotDatafile(shot) os_wrapper.createDir(os.path.dirname(shot_datafile)) if self.shot_data.load(shot_datafile): existing += 1 shots_existing += shot + "\n" elif self.shot_data.save(): success += 1 shots_created += shot + "\n" else: failure += 1 shots_failed += shot + "\n" if success: message = "%d %s created successfully: " % ( success, verbose.pluralise('shot', success)) dialog_msg += "%s\n%s\n" % (message, shots_created) verbose.message(message + shots_created) if existing: message = "The following %d shot(s) were not created as they already exist: " % existing dialog_msg += "%s\n%s\n" % (message, shots_existing) verbose.warning(message + shots_existing) if failure: message = "The following %d shot(s) could not be created - please check write permissions and try again: " % failure dialog_msg += "%s\n%s\n" % (message, shots_failed) verbose.error(message + shots_failed) # Confirmation dialog dialog_title = "Shot Creator Results" dialog = prompt.dialog() dialog.display(dialog_msg, dialog_title, conf=True) self.populateShots()
def change_version(input_version, value, absolute=False): """ Change the version number. If absolute is True, set the version to the given value. Otherwise, adjust the existing value by the given value. """ if absolute: new_version = value else: new_version = input_version + value # Ensure the new value is within the allowed range if __min_version__ <= new_version <= __max_version__: return new_version else: verbose.error("Version number out of bounds.") return input_version
def move(source, destination, quiet=False): """ Move a file or folder. """ src = os.path.normpath(source) dst = os.path.normpath(destination) if not quiet: verbose.print_('move "%s" -> "%s"' % (src, dst)) try: shutil.move(src, dst) return True except: exc_type, exc_value, exc_traceback = sys.exc_info() msg = traceback.format_exception_only(exc_type, exc_value)[0] if not quiet: verbose.error(msg) return False
def remove(path, quiet=False): """ Removes files or folders recursively. """ path = os.path.normpath(path) try: if os.path.isfile(path): os.remove(path) elif os.path.isdir(path): shutil.rmtree(path) return True, path except: exc_type, exc_value, exc_traceback = sys.exc_info() msg = traceback.format_exception_only(exc_type, exc_value)[0] if not quiet: verbose.error(msg) return False, msg
def addJob(self): """ Open the edit job dialog to add a new job. """ editJobDialog = edit_job.dialog(parent=self) if editJobDialog.display('', '$IC_JOBSROOT', os.environ['IC_VERSION'], True): if self.j.addJob(editJobDialog.jobName, editJobDialog.jobPath, editJobDialog.jobVersion, editJobDialog.jobActive): self.reloadJobs(reloadDatabase=False, selectItem=editJobDialog.jobName) else: errorMsg = "Could not create job as a job with the name '%s' already exists." % editJobDialog.jobName dialogMsg = errorMsg + "\nWould you like to create a job with a different name?" verbose.error(errorMsg) # Confirmation dialog dialogTitle = 'Job Not Created' dialog = prompt.dialog() if dialog.display(dialogMsg, dialogTitle): self.addJob()
def render_split(filename): # if filename.startswith('.'): # return # if not pblChk.paddingChk(filename): # return # nameBody, padding, extension = filename.split('.') # return nameBody, padding, extension # Parse filename try: base, ext = os.path.splitext(filename) prefix, framenumber = base.rsplit('.', 1) padding = len(framenumber) framenumber_int = int(framenumber) return prefix, framenumber, ext except ValueError: verbose.error("Could not parse sequence.") return # False, False, False # need to return tuple to match successful return type
def autoDeploy(): # Deploying commands organizer outputMsg = "Deploying GPS tools... " try: # Deploy commands organizer copying the file to user scenes directory # so it's not read directly from master file in pipeline. # There's a different commandsOrganizer.dat for osx/win due to file # path differences for icons. if os.environ['IC_RUNNING_OS'] == 'Windows': osdir = 'win' else: osdir = 'osx' src = os.path.join(os.environ['IC_BASEDIR'], 'rsc', 'realflow', 'realflow__env__', osdir, 'commandsOrganizer.dat') dst = os.path.join(os.environ['IC_REALFLOW_SCENES_DIR'], '.cmdsOrg', 'commandsOrganizer.dat') os_wrapper.copy(src, dst) verbose.message("%s Ok" % outputMsg) except: verbose.error("%s Failed" % outputMsg)
def editJob(self): """ Open edit job dialog. """ item = self.ui.jobs_listWidget.selectedItems()[0] jobName = item.text() editJobDialog = edit_job.dialog(parent=self) if editJobDialog.display(jobName, self.j.getPath(jobName), self.j.getVersion(jobName), self.j.getEnabled(jobName)): self.j.enableJob(jobName, editJobDialog.jobActive) self.j.setVersion(jobName, editJobDialog.jobVersion) self.j.setPath(jobName, editJobDialog.jobPath) if self.j.renameJob(jobName, editJobDialog.jobName): # Do this last as jobs are referenced by name self.reloadJobs(reloadDatabase=False, selectItem=editJobDialog.jobName) else: errorMsg = "Could not rename job as a job with the name '%s' already exists." % editJobDialog.jobName dialogMsg = errorMsg + "\nWould you still like to edit the job '%s'?" % jobName verbose.error(errorMsg) # Confirmation dialog dialogTitle = 'Job Not Created' dialog = prompt.dialog() if dialog.display(dialogMsg, dialogTitle): self.editJob()
def loadPanel(self, category): """ Load the panel UI (and helper module if required). The exec function is called here to avoid the error: 'unqualified exec is not allowed in function because it contains a nested function with free variables' with Python 2.x. """ ui_file = "settings_%s_ui.ui" % category helper_module = 'settings_%s' % category panel_ui_loaded = False helper_module_loaded = False # Create new frame to hold properties UI & load into frame self.ui.settings_frame.close() try: uifile = os.path.join(os.environ['IC_FORMSDIR'], ui_file) self.ui.settings_frame = QtCompat.loadUi(uifile) self.ui.settings_verticalLayout.addWidget(self.ui.settings_frame) panel_ui_loaded = True except FileNotFoundError: message = "Could not open '%s' properties panel UI. " % category verbose.error(message) # Load helper module try: exec_str = 'from . import %s as sh; helper = sh.helper(self, self.ui.settings_frame)' % helper_module # print(exec_str) exec(exec_str) helper_module_loaded = True except ImportError: message = "Could not import '%s' module. " % helper_module verbose.warning(message) if panel_ui_loaded: # and helper_module_loaded: return True else: return False
def submit_job(**kwargs): """ Submit job to Deadline. """ cmd_output = "" result_msg = "" # if kwargs is not None: # for key, value in kwargs.items(): # print("%24s = %s" %(key, value)) try: if kwargs['renderLayers']: # Batch submission ----------------------- # Generate submission info files num_jobs = 0 job_info_file_list = [] plugin_info_file_list = [] for render_layer in re.split( r',\s*', kwargs['renderLayers']): # may be better to pass as list kwargs['renderLayer'] = render_layer # kwargs['isMovie'] = False job_info_file = generate_job_info_file(**kwargs) job_info_file_list.append(job_info_file) plugin_info_file = generate_plugin_info_file(**kwargs) plugin_info_file_list.append(plugin_info_file) num_jobs += 1 # Generate batch file batch_submission_file = generate_batch_file( kwargs['scene'], job_info_file_list, plugin_info_file_list) # Execute deadlinecommand cmd_result, cmd_output = os_wrapper.execute( [os.environ['RQ_DEADLINECOMMAND'], batch_submission_file]) if cmd_result: result_msg = "Successfully submitted %d job(s) to Deadline." % num_jobs else: # Single job submission --------------------------------------- # Generate submission info files kwargs['renderLayer'] = None job_info_file = generate_job_info_file(**kwargs) plugin_info_file = generate_plugin_info_file(**kwargs) # Execute deadlinecommand cmd_result, cmd_output = os_wrapper.execute([ os.environ['RQ_DEADLINECOMMAND'], job_info_file, plugin_info_file ]) if cmd_result: result_msg = "Successfully submitted job to Deadline." if cmd_result: result = True verbose.print_(cmd_output) #.decode()) verbose.message(result_msg) else: raise RuntimeError(cmd_output) except: # Submission failed --------------------------------------------- result = False exc_type, exc_value, exc_traceback = sys.exc_info() traceback.print_exception(exc_type, exc_value, exc_traceback) result_msg = "Failed to submit job to Deadline." verbose.error(result_msg) if (exc_type == RuntimeError) and cmd_output: result_msg += "\n" + cmd_output else: result_msg += "\nCheck console output for details." #output_str = "Either the Deadline executable could not be found, or the submission info files could not be written." #output_str = traceback.format_exception_only(exc_type, exc_value)[0] return result, result_msg
def setupUI(self, window_object, window_title="", ui_file="", stylesheet="", prefs_file=None, store_window_geometry=True): """ Setup the UI. """ verbose.debug("Window object: %s Parent: %s" % (self, self.parent)) # Instantiate preferences data class if prefs_file is None: self.prefs = None else: self.prefs = self.createPrefs(prefs_file) # Define some global variables self.currentAttrStr = "" # Load UI & stylesheet found_ui_file = self.checkFilePath(ui_file, searchpath=[ os.environ['IC_FORMSDIR'], ]) if os.path.isfile(found_ui_file): self.ui = QtCompat.loadUi(found_ui_file, self) else: verbose.error("UI file does not exist: %s" % found_ui_file) # Store some system UI colours & define colour palette self.col = {} self.col['text'] = QtGui.QColor(204, 204, 204) self.col['disabled'] = QtGui.QColor(102, 102, 102) self.col['highlighted-text'] = QtGui.QColor(255, 255, 255) tmpWidget = QtWidgets.QWidget() self.col['sys-window'] = tmpWidget.palette().color( QtGui.QPalette.Window) self.col['sys-highlight'] = tmpWidget.palette().color( QtGui.QPalette.Highlight) # self.col['window'] = self.col['sys-window'] self.col['highlight'] = self.col['sys-highlight'] self.col['window'] = QtGui.QColor('#444444') # self.col['highlight'] = QtGui.QColor('#78909c') self.computeUIPalette() # Load and set stylesheet self.stylesheet = self.checkFilePath(stylesheet, searchpath=[ os.environ['IC_FORMSDIR'], ]) self.loadStyleSheet() # Set window title self.setObjectName(window_object) if window_title: self.setWindowTitle(window_title) else: window_title = self.windowTitle() # Perform custom widget setup self.setupWidgets(self.ui) # Restore window geometry and state self.store_window_geometry = store_window_geometry if self.store_window_geometry: try: uiName = self.objectName() if os.environ['IC_ENV'] != 'STANDALONE': uiName += "_" + os.environ['IC_ENV'].lower() self.settings = QtCore.QSettings(os.environ['IC_VENDOR'], uiName) self.restoreGeometry(self.settings.value("geometry", "")) verbose.print_("Restoring window geometry for '%s'." % self.objectName()) except (KeyError, TypeError): verbose.warning("Could not restore window geometry for '%s'." % self.objectName()) # # Use QSettings to store window geometry and state. # # (Restore state may cause issues with PyQt5) # if os.environ['IC_ENV'] == 'STANDALONE': # verbose.print_("Restoring window geometry for '%s'." %self.objectName()) # try: # self.settings = QtCore.QSettings( # os.environ['IC_VENDOR'], window_title) # self.restoreGeometry(self.settings.value("geometry", "")) # # self.restoreState(self.settings.value("windowState", "")) # except: # pass # # Makes Maya perform magic which makes the window stay on top in # # OS X and Linux. As an added bonus, it'll make Maya remember the # # window position. # elif os.environ['IC_ENV'] == 'MAYA': # self.setProperty("saveWindowPref", True) # elif os.environ['IC_ENV'] == 'NUKE': # pass # else: # # Move to centre of active screen # desktop = QtWidgets.QApplication.desktop() # screen = desktop.screenNumber(desktop.cursor().pos()) # self.move(desktop.screenGeometry(screen).center() - self.frameGeometry().center()) # # Move to centre of parent window # self.move(self.parent.frameGeometry().center() - self.frameGeometry().center()) # Set up keyboard shortcuts self.shortcutUnloadStyleSheet = QtWidgets.QShortcut(self) self.shortcutUnloadStyleSheet.setKey('Ctrl+Shift+R') self.shortcutUnloadStyleSheet.activated.connect(self.unloadStyleSheet) self.shortcutReloadStyleSheet = QtWidgets.QShortcut(self) self.shortcutReloadStyleSheet.setKey('Ctrl+R') self.shortcutReloadStyleSheet.activated.connect(self.loadStyleSheet)
def parse(filepath, base_dir=os.environ['SCNMGR_SAVE_DIR'], convention=os.environ['SCNMGR_CONVENTION']): """ Parse the given filepath (relative to base_dir) based on a naming convention and return a dictionary of elements for processing. TODO: check shot, artist, discipline etc. against valid whitelist """ if not os.path.isfile(filepath): verbose.print_("Could not parse filename as file doesn't exist: %s" % filepath) return None filepath = os.path.normpath(filepath) base_dir = os.path.normpath(base_dir) # Make filepath relative to base_dir if filepath.startswith(base_dir): filepath = filepath.replace(base_dir, '', 1) filepath = filepath.replace('\\', '/') # Convert to forward slashes if filepath.startswith('/'): filepath = filepath.replace('/', '', 1) # Remove leading slash # Find optional parts in naming convention # (only one optional section is allowed, if there are more, all optional # parts will be ignored.) valid_conventions = [convention.replace('[', '').replace(']', '')] pattern = r'\[.+?\]' optionals = re.findall(pattern, convention) if len(optionals) == 1: for i, optional in enumerate(optionals): valid_conventions.append(convention.replace(optional, '', i + 1)) # print valid_conventions # # Find tokens in naming convention # pattern = r'<\w+>' # tokens = re.findall(pattern, convention) # # Remove duplicates & sort list # tokens = list(set(tokens)) # # tokens.sort() # print tokens # # Generate regular expression to represent naming convention token_dict = {} success = False f = explode_path(filepath) for con in valid_conventions: c = explode_path(con) if same_structure(f, c): for i, dirs in enumerate(c): for j, token in enumerate(dirs): value = f[i][j] if token in token_dict: # Sanity check duplicated tokens if token_dict[token] != value: verbose.error( "Could not parse filename due to a token value mismatch." ) return None else: token_dict[token] = value success = True if success: # print(token_dict) return token_dict else: msg = "The filename '%s' could not be parsed because it does not comply with the naming convention." % filepath for con in valid_conventions: msg += "\n" + con verbose.error(msg) return None