def _replace_glow_mask(n, temp_channel='mask.a'): mask_knob = 'maskChannelMask' if n.input(1) else 'maskChannelInput' mask_channel = u(n[mask_knob].value()) if mask_channel == 'none': return False copy_node = nuke.nodes.Copy(inputs=(n.input(0), n.input(1)), from0=mask_channel, to0=temp_channel) copy_node.setXYpos(n.xpos(), n.ypos() - max(copy_node.screenHeight(), 32) - 10) width_channel = n['W'].value() if width_channel != 'none': input0 = nuke.nodes.ChannelMerge( inputs=(copy_node, copy_node), A=temp_channel, operation='in', B=width_channel, ) else: input0 = copy_node n.setInput(0, input0) n.setInput(1, None) n['maskChannelMask'].setValue('none') n['maskChannelInput'].setValue('none') n['W'].setValue(temp_channel) u_print('修正Glow节点mask: {}'.format(u(n.name()))) return True
def import_resource(self, dir_path): """Import footages from config dictionary.""" # Get all subdir dirs = list(x[0] for x in os.walk(dir_path)) self.task_step('导入素材') for dir_ in dirs: # Get footage in subdir LOGGER.info('文件夹 %s:', dir_) if not re.match(BATCH_CONFIG['dir_pat'], os.path.basename(dir_.rstrip('\\/'))): LOGGER.info('\t不匹配文件夹正则, 跳过') continue files = nuke.getFileNameList(utf8(dir_)) footages = [ u(i) for i in files if not u(i).endswith(('副本', '.lock')) ] if files else [] if footages: for f in footages: if os.path.isdir(e(os.path.join(dir_, f))): LOGGER.info('\t文件夹: %s', f) continue LOGGER.info('\t素材: %s', f) if re.match(CONFIG['footage_pat'], f, flags=re.I): n = nuke.createNode( 'Read', utf8('file {{{}/{}}}'.format(dir_, f))) n['on_error'].setValue(utf8('nearest frame')) else: LOGGER.info('\t\t不匹配素材正则, 跳过') LOGGER.info('{:-^30s}'.format('结束 导入素材')) if not nuke.allNodes('Read'): raise FootageError(dir_path)
def remove_duplicated_read(is_show_result=True): """Remove duplicated read to save memory. """ nodes = nuke.allNodes('Read') nodes.sort(key=lambda n: n.ypos()) distinct_read = [] removed_nodes = [] for n in nodes: same_node = _find_same(n, distinct_read) if same_node: dot = nuke.nodes.Dot(inputs=[same_node], label='代替: {}\n{}'.format( u(n.name()), u(nuke.filename(n))).encode('utf-8'), hide_input=True) dot.setXYpos(n.xpos() + 34, n.ypos() + 57) core.replace_node(n, dot) n_name = u(n.name()) removed_nodes.append(n_name) u_print('用 {0} 代替 {1} , 删除 {1}。'.format(u(same_node.name()), n_name)) nuke.delete(n) else: distinct_read.append(n) if not is_show_result: return if removed_nodes: nuke.message('合并时删除了{}个节点: \n{}'.format( len(removed_nodes), ', '.join(removed_nodes)).encode('utf-8')) else: nuke.message('没有发现重复读取节点。'.encode('utf-8'))
def delete_unused_nodes(nodes=None, message=False): """Delete all unused nodes.""" if nodes is None: nodes = nuke.allNodes() nodes = sorted(nodes, key=lambda n: (n.ypos(), n.xpos()), reverse=True) # Split disabled nodes. disabled_nodes = [n for n in nodes if _is_disabled_and_no_expression(n)] for n in disabled_nodes: node_name = u(n.name()) core.replace_node(n, n.input(0)) u_print('分离已禁用的节点: {}'.format(node_name)) # Delete unused nodes. is_used_result_cache = {} unused_nodes = [n for n in nodes if not _is_used(n, is_used_result_cache)] for n in unused_nodes: node_name = u(n.name()) nuke.delete(n) u_print('删除节点: {}'.format(node_name)) u_print('删除了 {} 个无用节点.'.format(len(unused_nodes))) if message: nuke.message( '<font size=5>删除了 {} 个未使用的节点。</font>\n' '<i>名称以"_"(下划线)开头的节点及其上游节点将不会被删除</i>'.format( len(unused_nodes)).encode('utf-8'))
def append_knob(node, knob): """Add @knob as @node's last knob. """ knob_name = u(knob.name()) node_name = u(node.name()) if nuke.exists('{}.{}'.format(node_name, knob_name).encode('utf-8')): knob.setValue(node[knob_name.encode('utf-8')].value()) node.removeKnob(node[knob_name.encode('utf-8')]) node.addKnob(knob)
def get_filenames(url): ret = [] if not os.path.isdir(e(url)): return ret for dirpath, _, _ in os.walk(e(url)): dirpath = u(dirpath.replace('\\', '/')) filenames = nuke.getFileNameList(e(dirpath, 'UTF-8')) filenames = ['{}/{}'.format(dirpath, u(i)) for i in filenames] ret.extend(filenames) return ret
def _on_precomp_name_changed(self, knob): rootpath = PurePath(u(nuke.value('root.name'))) name = u(knob.value()) or 'precomp1' script_path = (rootpath.parent / ''.join([rootpath.stem] + ['.{}'.format(name)] + rootpath.suffixes)).as_posix() render_path = 'precomp/{0}/{0}.%04d.exr'.format( ''.join([rootpath.stem] + ['.{}'.format(name)] + [i for i in rootpath.suffixes if i != '.nk'])) self.scriptPath.setValue(script_path.encode('utf-8')) self.renderPath.setValue(render_path.encode('utf-8'))
def replace_sequence(): # TODO: Need refactor and test. '''Replace all read node to specified frame range sequence. ''' # Prepare Panel panel = nuke.Panel(b'单帧替换为序列') label_path_prefix = b'限定只替换此文件夹中的读取节点' label_first = b'设置工程起始帧' label_last = b'设置工程结束帧' panel.addFilenameSearch(label_path_prefix, 'z:/') panel.addExpressionInput(label_first, int(nuke.Root()['first_frame'].value())) panel.addExpressionInput(label_last, int(nuke.Root()['last_frame'].value())) confirm = panel.show() if confirm: render_path = os.path.normcase(u(panel.value(label_path_prefix))) first = int(panel.value(label_first)) last = int(panel.value(label_last)) flag_frame = None nuke.Root()[b'proxy'].setValue(False) nuke.Root()[b'first_frame'].setValue(first) nuke.Root()[b'last_frame'].setValue(last) for n in nuke.allNodes('Read'): file_path = u(nuke.filename(n)) if os.path.normcase(file_path).startswith(render_path): search_result = re.search(r'\.([\d]+)\.', file_path) if search_result: flag_frame = search_result.group(1) file_path = re.sub( r'\.([\d#]+)\.', lambda matchobj: r'.%0{}d.'.format(len(matchobj.group(1))), file_path) n[b'file'].setValue(file_path.encode('utf-8')) n[b'format'].setValue(b'HD_1080') n[b'first'].setValue(first) n[b'origfirst'].setValue(first) n[b'last'].setValue(last) n[b'origlast'].setValue(last) n = wlf_write_node() if n: if flag_frame: flag_frame = int(flag_frame) n[b'custom_frame'].setValue(flag_frame) nuke.frame(flag_frame) n[b'use_custom_frame'].setValue(True)
def throtted_warning(msg): """Only show each warning message once. """ msg = u(msg) if msg not in SHOWED_WARNING: nuke.warning(utf8(msg)) SHOWED_WARNING.append(msg)
def upload_image(filename, folder, token): """Upload image to server. Args: filename (str): Filename. folder (str): Server upload folder, usually same with project name. token (str): Server session token. Returns: ImageInfo: Uploaded image information. """ filename = u(filename) basename = os.path.basename(filename) data = post('web_upload_file', { 'folder': folder, 'type': 'project', 'method': 'convert_image', 'filename': basename }, token=token, files={ 'file': (basename, open(e(filename), 'rb'), mimetypes.guess_type(basename)[0]) }) assert isinstance(data, dict), type(data) data['path'] = filename return ImageInfo(**data)
def get_shot_list(self): """Return shot_list generator from a config dict.""" _dir = self.input_dir _out_dir = self.output_dir if not os.path.isdir(_dir): return [] _ret = os.listdir(_dir) if isinstance(_ret[0], str): _ret = tuple(u(i) for i in _ret) self._all_shots = _ret if self.flags & IGNORE_EXISTED: _ret = (i for i in _ret if not os.path.exists(os.path.join(_out_dir, u'{}_v0.nk'.format(i))) and not os.path.exists(os.path.join(_out_dir, u'{}.nk'.format(i)))) _ret = (i for i in _ret if ( re.match(CONFIG['dir_pat'], i) and os.path.isdir(os.path.join(_dir, i)))) if not _ret: _dir = _dir.rstrip('\\/') _dirname = os.path.basename(_dir) if re.match(CONFIG['dir_pat'], _dir): _ret = [_dir] return sorted(_ret)
def __new__(cls, filename, frame_ranges=None): # Skip new from other Asset objet. if isinstance(filename, Footage): return filename frame_ranges = filename if frame_ranges is None else frame_ranges filename = cls.filename_factory(filename) # Try find cached asset. for i in core.CACHED_ASSET: assert isinstance(i, Footage) if u(i.filename) == u(filename): if filename.with_frame(1) != filename.with_frame(2): i.frame_ranges += FrameRanges(frame_ranges) return i return super(Footage, cls).__new__(cls)
def process(self, instance): filename = instance.data['name'] if nuke.numvalue('preferences.wlf_send_to_dir', 0.0): render_dir = u(nuke.value('preferences.wlf_render_dir')) copy(filename, render_dir + '/') else: self.log.info('因为首选项设置而跳过')
def dropdata_handler(mime_type, data, hook): """Handling dropdata.""" if mime_type != 'text/plain': return None data = u(data) if hook.is_ignore_data(data=data): return None LOGGER.debug('Handling dropdata: %s %s', mime_type, data) urls = chain([data], *hook.get_url(data=data)) filenames = chain(*(chain([i], *hook.get_filenames(url=i)) for i in urls)) try: ret = None for filename in progress(tuple(filenames)): if hook.is_ignore_filename(filename=filename): LOGGER.debug('Ignore filename: %s', filename) ret = True continue LOGGER.debug('Handling filename: %s', filename) context = {'is_created': False} nodes = tuple( chain(*hook.create_node(filename=filename, context=context))) if any(nodes): ret = True LOGGER.debug('Created nodes: %s', nodes) hook.after_created(nodes=nodes) return ret except CancelledError: return True
def escape_for_channel(text): """Escape text for channel name. Args: text (str): Text for escaped Returns: str: Esacped text. Example: >>> escape_for_channel('apple') 'mask_extra.apple' >>> escape_for_channel('tree.apple') 'tree.apple' >>> escape_for_channel('tree.apple.leaf') 'tree.apple_leaf' >>> escape_for_channel('tree.apple.leaf.ца╣') 'tree.apple_leaf_?' >>> escape_for_channel(None) 'mask_extra.None' """ ret = u(text) if '.' not in ret: ret = 'mask_extra.{}'.format(ret) ret = ret.replace(' ', '_') ret = '{0[0]}{0[1]}{1}'.format( ret.partition('.')[:-1], ret.partition('.')[-1].replace('.', '_')) ret = ret.encode('ascii', 'replace') return ret
def l10n(self, value): """Return translated value. """ if not value: return '' for pat in self._translate_dict: if re.match(pat, value): return re.sub(pat, self._translate_dict[pat], value) return u(value)
def process(self, instance): is_ok = True for i in instance: assert isinstance(i, FootageInfo) if not os.path.normcase(u(i.filename)).startswith(self.valid_dir): self.log.error('使用了本地素材: %s', i.filename) is_ok = False if not is_ok: raise ValueError('Local file used.')
def _gizmo_to_group_update_ui(): n = nuke.thisNode() _temp_knob_name = 'wlf_gizmo_to_group' _has_temp_knob = nuke.exists( utf8('{}.{}'.format(u(n.name()), _temp_knob_name))) if _has_temp_knob: n = edit.gizmo_to_group(n) n.removeKnob(n[_temp_knob_name]) n.removeKnob(n['User'])
def process(self, instance): assert isinstance(instance, pyblish.api.Instance) dest = instance.context.data['workfileFileboxInfo'].path + '/' for n in nuke.allNodes('Precomp'): src = u(nuke.filename(n)) if src.startswith(dest.replace('\\', '/')): continue n['file'].setValue(copy(src, dest)) nuke.scriptSave()
def _shuffle(): channels = dict.fromkeys(['in', 'in2', 'out', 'out2'], '') for i in channels.keys(): channel_value = u(nuke.value('this.' + i)) if channel_value != 'none': channels[i] = channel_value + ' ' label = (channels['in'] + channels['in2'] + '-> ' + channels['out'] + channels['out2']).rstrip(' ') ret = _add_to_autolabel(label) return ret
def _knob_changed(self, knob): { self.precompName: _on_precomp_name_changed, }.get(knob, lambda *_: None)(self, knob) options = {name: k.value() for name, k in self.knobs().items()} options = { k: u(v) if isinstance(v, six.binary_type) else v for k, v in options.items() } PatchPrecompSelected.current_options = options assert PatchPrecompSelected.current_options
def _add_to_autolabel(text, center=False): if not isinstance(text, (str, unicode)): return ret = u(autolabel()).split('\n') ret.insert(1, text) ret = '\n'.join(ret).rstrip('\n') if center: ret = ('<div align="center" ' 'style="margin:0px;padding:0px">{}</div>').format(ret) return ret
def _is_used(n, cache): assert isinstance(cache, dict) node_name = u(n.name()) if cache.has_key(n): return cache[n] if (node_name.startswith('_') or node_name == 'VIEWER_INPUT' or u(n.Class()) in ('BackdropNode', 'Read', 'Write', 'Viewer', 'GenerateLUT', 'wlf_Write')): ret = True else: ret = (not _is_disabled_and_no_expression(n) and any(_is_used(n, cache) for n in n.dependent())) cache[n] = ret return ret
def process(self, instance): context = instance.context task = self.get_task(context) assert isinstance(task, Task) n = wlf_write_node() path = u(nuke.filename(n.node('Write_JPG_1'))) dest = task.filebox.get('image').path + '/{}.jpg'.format(task.shot) # dest = 'E:/test_pyblish/{}.jpg'.format(task.shot) copy(path, dest) context.data['submitImage'] = task.set_image(dest)
def __init__(self, node): assert isinstance(node, nuke.Node) n = node self._filename = u(nuke.filename(n)) path = PurePath(self._filename) tag = (nuke.value( '{}.{}'.format(u(n.name()), self.tag_knob_name).encode('utf-8'), '') or path.tag) k = nuke.String_Knob(self.tag_knob_name, '标签'.encode('utf-8')) append_knob(node, k) k.setValue(tag) layer = path.layer k = nuke.String_Knob(self.layer_knob_name, '层'.encode('utf-8')) append_knob(node, k) k.setValue(path.layer) n.setName('_'.join(i for i in (tag, layer) if i).encode('utf-8'), updateExpressions=True)
def _run(asset): assert isinstance(asset, Footage) try: missing_frames = asset.missing_frames() if missing_frames: key = u(asset.filename.as_posix()) if key in ret: ret[key].add(missing_frames) else: ret[key] = missing_frames except: import traceback raise RuntimeError(traceback.format_exc())
def _get_mtime_info(): ret = {} for n in nuke.allNodes('Read', nuke.Root()): try: mtime = time.mktime( time.strptime(n.metadata('input/mtime'), '%Y-%m-%d %H:%M:%S')) except TypeError: continue if mtime > since: ret[nuke.filename(n)] = mtime ftime = time.strftime('%m-%d %H:%M:%S', time.localtime(mtime)) throtted_warning('{}: [new footage]{}'.format( u(n.name()), ftime)) return ret
def mark_enable(nodes): """Mark nodes enable later then disabled them. """ if isinstance(nodes, nuke.Node): nodes = (nodes, ) for n in nodes: try: label_knob = n['label'] label = u(label_knob.value()) if ENABLE_MARK not in label: label_knob.setValue('{}\n{}'.format( label, ENABLE_MARK).encode('utf-8')) n['disable'].setValue(True) except NameError: continue
def create_out_dirs(node=None): """Create this read node's output dir if need.""" n = node or nuke.thisNode() try: if n['disable'].value(): return except NameError: pass filename = u(nuke.filename(n)) if filename: target_dir = e(os.path.dirname(filename)) if not os.path.isdir(target_dir): LOGGER.debug('Create dir: %s', target_dir) os.makedirs(target_dir)
def glow_no_mask(temp_channel='mask.a', is_show_result=True): """Use width channel on `Glow2` node, instead of mask. """ channel.add_channel(temp_channel) nodes = nuke.allNodes('Glow2') result = [n for n in nodes if _replace_glow_mask(n, temp_channel)] if not is_show_result: return if result: nuke.message('将{}个Glow节点的mask更改为了width channel:\n{}'.format( len(result), ','.join(u(n.name()) for n in result)).encode('utf-8')) else: nuke.message('没有发现使用了mask的Glow节点。'.encode('utf-8'))