def commander_execute(self, msg, flags): # Stop recording lx.eval('replay.record stop') macro = replay.Macro() # Hack to fix crash bug. macro.select_event_treeview() if not macro.file_path: return if not macro.unsaved_changes: return if modo.dialogs.yesNo(message("MECCO_REPLAY", "REVERT_DIALOG_TITLE"), message("MECCO_REPLAY", "REVERT_FILE_MSG")) == 'yes': # No unsaved changes macro.unsaved_changes = False # Reload saved data macro.parse('open', macro.file_path) # Rebuild treeview macro.rebuild_view() notifier = replay.Notifier() notifier.Notify(lx.symbol.fCMDNOTIFY_CHANGE_ALL)
def commander_execute(self, msg, flags): # Register Undo object performing operation and apply it macro = replay.Macro() if macro.all_suppressed(): modo.dialogs.alert(message("MECCO_REPLAY", "EMPTY_MACRO"), message("MECCO_REPLAY", "EMPTY_MACRO_MSG"), dtype='warning') return undo_svc = lx.service.Undo() if undo_svc.State() != lx.symbol.iUNDO_INVALID: step = UndoStep() try: step.undo_Forward() except: return undo_svc.Record(step)
def commander_execute(self, msg, flags): """Saves the current Macro() object to the destination stored in its `file_path` property. If `file_path` is `None`, prompt for a destination. Unlike `replay.fileSave` this command can save in multiple formats.""" # Stop recording lx.eval('replay.record stop') macro = replay.Macro() format_val = self.commander_arg_value(0) file_path = self.commander_arg_value(1) if file_path is None: file_path = modo.dialogs.customFile( dtype='fileSave', title=message("MECCO_REPLAY", "EXPORT_DIALOG_TITLE"), names=macro.export_format_names, unames=macro.export_format_unames, ext=macro.export_format_extensions, path=self._path) if file_path is None: return self.__class__._path = file_path format_val = lx.eval('dialog.fileSaveFormat ?') replay.Macro().render(format_val, file_path)
def commander_execute(self, msg, flags): # Stop recording lx.eval('replay.record stop') macro = replay.Macro() if macro.file_path: lx.eval('cmds.mapKey command:{replay.runScript {%s}}' % macro.file_path) else: default_path = lx.eval('query platformservice alias ? {scripts:}') # Get the path from the user, if not given as argument: file_path = modo.dialogs.customFile( dtype='fileOpen', title=message("MECCO_REPLAY", "KEY_MAPPING_SCRIPT"), names=macro.import_format_names, unames=macro.import_format_unames, patterns=macro.import_format_patterns, path=default_path) if file_path is None: return lx.eval('cmds.mapKey command:{@{%s}}' % file_path)
def commander_execute(self, msg, flags): # Stop recording lx.eval('replay.record stop') file_path = replay.Macro().file_path if file_path is None: lx.eval('replay.saveAs') try: file_path = replay.Macro().file_path lx.eval('!!file.open {%s}' % file_path) lx.eval('replay.fileClose') except: modo.dialogs.alert( message("MECCO_REPLAY", "OPEN_FILE_FAIL"), message("MECCO_REPLAY", "OPEN_FILE_FAIL_MSG", basename(file_path)))
def commander_execute(self, msg, flags): prompt_save = self.commander_arg_value(0, True) # Stop recording lx.eval('replay.record stop') macro = replay.Macro() # Hack to fix crash bug. macro.select_event_treeview() # If content is not empty ask user for save if prompt_save and macro.unsaved_changes and not macro.is_empty: file_path = macro.file_path if file_path is None: file_path = "Untitled" if modo.dialogs.yesNo( message("MECCO_REPLAY", "ASK_FOR_SAVE_DIALOG_TITLE"), message("MECCO_REPLAY", "ASK_FOR_SAVE_DIALOG_MSG")) == 'yes': # If file path is not assigned ask for new file path if macro.file_path is None: file_path = modo.dialogs.customFile(dtype = 'fileSave', title = message("MECCO_REPLAY", "SAVE_DIALOG_TITLE"), \ names = ('LXM',), unames = ('LXM file'), ext=('LXM',)) if file_path is None: return macro.render_LXM(file_path) # No more file path macro.file_path = None macro.file_format = None macro.unsaved_changes = False # Clear current macro macro.clear() # Rebuild treeview macro.rebuild_view() notifier = replay.Notifier() notifier.Notify(lx.symbol.fCMDNOTIFY_CHANGE_ALL)
def commander_execute(self, msg, flags): # Stop recording lx.eval('replay.record stop') # Open the replay palette lx.eval('layout.createOrClose ReplayPalette {ReplayPalette} true {Replay Palette} width:400 height:600 persistent:true style:palette') # Try to get the path from the command line: input_path = self.commander_arg_value(0) macro = replay.Macro() # Get the path from the user, if not given as argument: if not input_path: input_path = modo.dialogs.customFile( dtype = 'fileOpen', title = message("MECCO_REPLAY", "OPEN_DIALOG_TITLE"), names = macro.import_format_names, unames = macro.import_format_unames, patterns = macro.import_format_patterns, path = self._path ) if input_path is None: return self.__class__._path = input_path # Parse the file in replay.Macro() and rebuild the view: try: macro.parse('open', input_path) # If successfully parsed add to recently-opened lx.eval('replay.fileOpenAddRecent {%s}' % input_path) except Exception as err: lx.out("Error ", str(err)) modo.dialogs.alert(message("MECCO_REPLAY", "OPEN_FILE_FAIL"), message("MECCO_REPLAY", "OPEN_FILE_FAIL_MSG", str(err)), dtype='warning') finally: macro.rebuild_view() notifier = replay.Notifier() notifier.Notify(lx.symbol.fCMDNOTIFY_CHANGE_ALL)
def commander_execute(self, msg, flags): # Stop recording lx.eval('replay.record stop') # Try to get the path from the command line: input_path = self.commander_arg_value(0) macro = replay.Macro() # Get the path from the user, if not given as argument: if not input_path: input_path = modo.dialogs.customFile( dtype='fileOpen', title=message("MECCO_REPLAY", "OPEN_DIALOG_TITLE"), names=macro.import_format_names, unames=macro.import_format_unames, patterns=macro.import_format_patterns, path=self._path) if input_path is None: return self.__class__._path = input_path # Parse the file in replay.Macro() and rebuild the view: try: macro.parse('insert', input_path) replay.Macro().unsaved_changes = True except Exception as err: modo.dialogs.alert(message("MECCO_REPLAY", "OPEN_FILE_FAIL"), message("MECCO_REPLAY", "OPEN_FILE_FAIL_MSG", str(err)), dtype='warning') finally: macro.rebuild_view() notifier = replay.Notifier() notifier.Notify(lx.symbol.fCMDNOTIFY_CHANGE_ALL)
def list_scripts(self): path = lx.eval('query platformservice alias ? {%s}' % self.commander_arg_value(0)) commands_list = [] for sub in [ f for f in os.listdir(path) if is_valid_script(os.path.join(path, f)) ]: commands_list.append((os.path.join(path, sub), os.path.basename(sub))) commands_list.append(('', '(%s)' % message("MECCO_REPLAY", "REFRESH"))) return commands_list
def commander_execute(self, msg, flags): # Try to get the path from the command line: input_path = self.commander_arg_value(0) macro = replay.Macro() # Get the path from the user, if not given as argument: if not input_path: input_path = modo.dialogs.customFile( dtype = 'fileOpen', title = message("MECCO_REPLAY", "RUN_DIALOG_TITLE"), names = macro.import_format_names, unames = macro.import_format_unames, patterns = macro.import_format_patterns ) if input_path is None: return if macro.file_path == input_path and macro.unsaved_changes and not macro.is_empty: if modo.dialogs.yesNo(message("MECCO_REPLAY", "ASK_FOR_SAVE_BEFORE_RUN_DIALOG_TITLE"), message("MECCO_REPLAY", "ASK_FOR_SAVE_BEFORE_RUN_DIALOG_MSG")) == 'yes': macro.render(macro.file_format, macro.file_path) lx.eval('@{%s}' % input_path)
def commander_execute(self, msg, flags): comment = self.commander_arg_value(0) macro = replay.Macro() # Check if selection exists selecteds = macro.selected_descendants if len(selecteds) == 0: modo.dialogs.alert(message("MECCO_REPLAY", "NO_SELECTED_COMMAND_MSG"), dtype='warning') return # Collect list of selected command paths paths = list() for sel in selecteds: paths.append(sel.path) # Register Undo object performing operation and apply it undo_svc = lx.service.Undo() if undo_svc.State() != lx.symbol.iUNDO_INVALID: undo_svc.Apply(UndoInsertComment(paths, comment))
def commander_execute(self, msg, flags): # Stop recording lx.eval('replay.record stop') macro = replay.Macro() file_path = None file_format = macro.file_format # If there is no associated file path try to get from command line or prompt the user for new destination if file_path is None: # Try to get the path from the command line: file_path = self.commander_arg_value(0) file_format = "lxm" # Prompt the user if not file_path: file_path = modo.dialogs.customFile(dtype='fileSave', title=message( "MECCO_REPLAY", "SAVE_DIALOG_TITLE"), names=('LXM', ), unames=('LXM file', ), ext=('LXM', ), path=self._path) if file_path is None: return self.__class__._path = file_path # And save it for the next time macro.file_path = file_path macro.render(file_format, file_path) lx.eval('!!replay.fileClose') lx.eval('replay.fileOpen {%s}' % file_path) # Add to recently-opened lx.eval('replay.fileOpenAddRecent {%s}' % file_path)
def cmd_Query(self, index, vaQuery): """Fires whenever the value is displayed in the form. Should return the value(s) to be displayed in the edit field. If multiple values are provided, MODO will display "mixed..." in the edit field for batch editing.""" # Create the ValueArray object va = lx.object.ValueArray() va.set(vaQuery) if index != 1: return lx.result.OK # GATHER VALUES # ------------- argName = self.commander_args()['argName'] datatype, hints, default = self.arg_info(1) argValues = set() for arg in self.args_by_argName(argName): argValues.add((arg.value)) # If there are no values to return, don't bother. if not argValues: return lx.result.OK values_list = list(argValues) # RETURN VALUES # ------------- # Need to add the proper datatype based on result from commander_query for value in values_list: # Sometimes we get passed empty values. Ignore those. if value is None: value = default if value is None: continue # Sadly, I am not aware of any way of handling datatypes except # by manually testing for them and doing the appropriate action. # MODO doesn't make this easy. # Strings if datatype in [ lx.symbol.sTYPE_DATE, lx.symbol.sTYPE_DATETIME, lx.symbol.sTYPE_FILEPATH, lx.symbol.sTYPE_STRING, lx.symbol.sTYPE_VERTMAPNAME ]: try: va.AddString(str(value)) except: raise Exception(message("MECCO_REPLAY", "INVALID_STRING"), value, type(value), datatype) # Text Value Hints elif (datatype == lx.symbol.sTYPE_INTEGER) and hints: for idx, name in hints: if name == value: va.AddInt(idx) # Booleans elif datatype == lx.symbol.sTYPE_BOOLEAN: va.AddInt(1 if value.lower() in ['true', 'on', 'yes'] else 0) # Integers elif datatype in [ lx.symbol.sTYPE_INTEGER, lx.symbol.sTYPE_BOOLEAN ]: va.AddInt(int(value if value else 0)) # Floats elif datatype in [ lx.symbol.sTYPE_ACCELERATION, lx.symbol.sTYPE_AXIS, lx.symbol.sTYPE_COLOR1, lx.symbol.sTYPE_FLOAT, lx.symbol.sTYPE_FORCE, lx.symbol.sTYPE_LIGHT, lx.symbol.sTYPE_DISTANCE, lx.symbol.sTYPE_MASS, lx.symbol.sTYPE_PERCENT, lx.symbol.sTYPE_SPEED, lx.symbol.sTYPE_TIME, lx.symbol.sTYPE_UVCOORD ]: va.AddFloat(float(value)) # Angles need to be converted to radians elif datatype in [lx.symbol.sTYPE_ANGLE]: va.AddFloat(math.radians(float(value))) # Vectors (i.e. strings that need parsing) elif datatype in [ lx.symbol.sTYPE_ANGLE3, lx.symbol.sTYPE_COLOR, lx.symbol.sTYPE_DISTANCE3, lx.symbol.sTYPE_FLOAT3, lx.symbol.sTYPE_PERCENT3, '&item' ]: emptyValue = va.AddEmptyValue() emptyValue.SetString(str(value)) # If the datatype isn't handled explicitly above, we try adding it # to a generic value object. Failing that, barf. else: try: va.AddValue(value) except: raise Exception( message("MECCO_REPLAY", "QUERY_DATATYPE_DETECT_ERROR")) return lx.result.OK
def valid_for_record(self, cmd, isResult=False): # Recording is disabled by user. if not self.state: return False # Recording `layout.createOrClose` is optional. It's important that we not only # skip the command itself, but also any sub-commands within it. As such, we disarm # until isResult. (Hence `if not self.armed:` happens _after_ this check.) if cmd.Name( ) == 'layout.createOrClose' and not self.layoutCreateOrClose: self.debug_path_print( cmd.Name() + " - Recording disabled by preference. Ignore.") self.armed = False if isResult: self.armed = True return False # Recording is disarmed for internal reasons. if not self.armed: return False # Never record "quiet" commands. if (cmd.Flags() & lx.symbol.fCMD_QUIET): self.debug_path_print(cmd.Name() + " - Quiet command. Ignore.") return False # Never record replay commands. if cmd.Name().startswith("replay."): self.debug_path_print(cmd.Name() + " - Replay command. Ignore.") return False # BLACK LIST # Certain commands can be safely ignored. These can be added here. # Note that any ignored command's sub-commands _will_ be recorded. if cmd.Name() in [ 'tool.attr', 'tool.noChange', 'actionCenter.state', 'workPlane.state', 'falloff.state', 'layout.restore', 'view3d.toggleHUD' ]: self.debug_path_print(cmd.Name() + " - Black list. Ignore.") return False # We cannot record undo/redo. There is no reliable method of doing so. # Instead, we simply stop recording. if cmd.Name() in ['app.undo', 'app.redo']: if isResult: self.queue_lazy_visitor( replay_record_kill, message("MECCO_REPLAY", "UNDO_DURING_RECORDING"), message("MECCO_REPLAY", "CANNOT_RECORD_MSG", cmd.Name())) return False # We cannot record interactive selections (i.e. clicking in the viewport to select). # Instead, we simply warn the user and stop recording. # NOTE: This can cause crashes. Be careful. if cmd.Name() in ['select.paint', 'select.lasso']: if isResult: self.queue_lazy_visitor( replay_record_kill, message("MECCO_REPLAY", "INTERACTIVE_DURING_RECORDING"), message("MECCO_REPLAY", "CANNOT_RECORD_MSG", cmd.Name())) return False # If we pass all of the above tests, we're good to record. return True
# python import lx, lxifc, modo, replay from replay import message as message """A simple example of a blessed MODO command using the commander module. https://github.com/adamohern/commander for details""" PREFIXES = [('', message("MECCO_REPLAY", "PREFIX_NONE")), ('!', message("MECCO_REPLAY", "PREFIX_SUPPRESS_DIALOGS")), ('!!', message("MECCO_REPLAY", "PREFIX_SUPPRESS_ALL_DIALOGS")), ('+', message("MECCO_REPLAY", "PREFIX_SHOW_DIALOGS")), ('++', message("MECCO_REPLAY", "PREFIX_SHOW_ALL_DIALOGS")), ('q', message("MECCO_REPLAY", "PREFIX_SHOW_COMMAND_DIALOG"))] class CommandClass(replay.commander.CommanderClass): """Sets the prefix for the currently selected command(s) to one of the standard dialog prefixes: `'!'` - Suppress dialogs. `'!!'` - Suppress all dialogs. `'+'` - Show dialogs. `'++'` - Show all dialogs. `'?'` - Show command dialog. NOTE: Since commands cannot accept `?` as an argument, use `q` for `?`. See http://sdk.luxology.com/wiki/Command_System:_Executing#Special_Prefixes""" def commander_arguments(self): return [{ 'name': 'command_prefix',
def prepare_action_list(self, mode, index): # Checking mode validity if mode not in ['up', 'down', 'top', 'bottom', 'index']: modo.dialogs.alert(message("MECCO_REPLAY", "INVALID_MODE"), message("MECCO_REPLAY", "INVALID_MODE_MSG", mode), dtype='warning') return None macro = replay.Macro() # Checking index range if (mode == 'index') and (index >= len(macro.children)): modo.dialogs.alert(message("MECCO_REPLAY", "OUT_OF_RANGE"), message("MECCO_REPLAY", "OUT_OF_RANGE_MSG", index), dtype='warning') return None # Checking if selection exists if len(macro.selected_children) == 0: modo.dialogs.alert(message("MECCO_REPLAY", "NO_SELECTED_COMMAND"), message("MECCO_REPLAY", "NO_SELECTED_COMMAND_MSG"), dtype='warning') return None actionList = MoveActionList() # Getting sel_children = macro.selected_children # If going up, we move up starting with the top of the list and move down. if mode == "up": for child in sel_children: actionList.append(child.index, child.index - 1) elif mode == "down": # If going any other direction, start sel_children.sort(key=lambda x: x.index, reverse=True) for child in sel_children: actionList.append(child.index, child.index + 1) elif mode == "top": for child in sel_children: actionList.append(child.index, 0) elif mode == "bottom": # If going any other direction, start sel_children.sort(key=lambda x: x.index, reverse=True) for child in sel_children: actionList.append(child.index, len(child.parent.children) - 1) elif mode == "index": # Sort all children standing below target index in reverse order sel_children_above = [x for x in sel_children if x.index > index] sel_children_below = [x for x in sel_children if x.index <= index] sel_children_below.sort(key=lambda x: x.index, reverse=True) for child in sel_children_above + sel_children_below: actionList.append(child.index, index) return actionList