Isotropix Forums

Automated File Management Tool

Clarisse Scripting related topics

Automated File Management Tool

Unread postby dane » Fri Jan 03, 2020 8:35 pm

Hey people,

I've stripped this out of our Clarisse toolset I've been building sparadically over the last year or so. It acts as a basic way of doing file management in a vfx pipeline. It acts as a good building block for adding your own tools and automation of things like render paths, reference management etc

>>> Project structure

A normal pipeline structure is as such:

project > episode > sequence > shot

This is an episodic example but with film you tend to use the same way of organising things.

>>>Using the tool

Screenshot 2020-01-03 at 19.29.11.png

When you launch the tool the first thing you'll see is a place to browse to a shot. So we want to navigate to our shot directory.

The tool works by splitting up this path and making variables in Clarisse for project/ep/seq/shot/task. Using this we can restructure new paths on disk and create file naming etc So once you've set your path and chosen your task, nothing has happened yet, from here you can use the open button and it will take you to where the files are saved for that shot (if there are some). This isn't done using variables. The reason is if you are working on a shot already then you'd end up setting the wrong variables in that shot to open another shot.

When you are ready to save hit the save button, at this point the variables are set and everything can be made from the variables in your own tools, to build whatever paths or naming you want.

Screenshot 2020-01-03 at 19.29.23.png

The same save button will also version up your script by looking in the folder and finding the last version and going up from there. So if you are on v006, go back to v002 then hit save, it will be v007, not v003.

The open shot directory button simply opens an OS file browser at the shot location.

>>> Adjusting the script for your own pipeline

As your structure may be different you'll have to adjust parts of the code to take the right sections of the path for each variable.
In parts you'll see where I'm taking the path and joining it with some new folders, so you can adjust those if you want something different.
You can add more tasks in the list if needed. Also this uses the user variable already given by the os so you can use the current script with artists working on multiple shots and it won't clash.

If anyone wants help adjusting the script then just ask on here or use my email in the script.

Its worth saying I've only tested this on Linux and MacOS, not windows, I think it should work on Windows though...

Here is the script, enjoy!

py code

File Management Toolset

This is a basic automated file handing script that, with some adjustment to fit into your
own folder structure, should save you a lot of time with file management.

Full explination will be put with the post on the clarisse forum on how to adjust and use
this script

Daniel Newlands, 2019,

import os
import shutil
import re

#tasks to be listed in gui
TASKS = ('light', 'lookdev', 'previs')

#global variables to be used in all tools once set by save
def getSetVars(self, sender, evtid):

taskName = ix.application.get_factory().get_vars().get("TASK").get_string()
shotName = ix.application.get_factory().get_vars().get("SHOT").get_string()
sequenceName = ix.application.get_factory().get_vars().get("SEQUENCE").get_string()
episodeName = ix.application.get_factory().get_vars().get("EPISODE").get_string()
projectName = ix.application.get_factory().get_vars().get("PROJECT").get_string()
filepath = ix.application.get_factory().get_vars().get("SHOT_DIR").get_string()
userName = ix.application.get_factory().get_vars().get("USER").get_string()

return taskName, shotName, sequenceName, episodeName, projectName, filepath, userName

class EventRewire(ix.api.EventObject):

def getShotDir(self, sender, evtid):

directory = ix.api.GuiWidget.open_folder(ix.application, '',
'Target directory') # Summon the window to choose files
if directory:
if os.path.isdir(str(directory)):
ix.log_warning("Invalid directory: %s" % str(directory))

def setShotDir(self, sender, evtid):

filepath = path_txt.get_text()
splitGlobalPath = filepath.split(os.sep)
projectName, episodeName, sequenceName, shotName = splitGlobalPath[-5], splitGlobalPath[-4], splitGlobalPath[-3], splitGlobalPath[-2]
projectName = os.path.basename(projectName).split('_')
projectName = os.sep.join(projectName[-1:])

#set shot var
directory = path_txt.get_text()
shotVar = ix.application.get_factory().get_vars().add(ix.api.OfVars.TYPE_CUSTOM, "SHOT_DIR", ix.api.OfAttr.TYPE_STRING)

#make vars
pathDir = ix.application.get_factory().get_vars().add(ix.api.OfVars.TYPE_CUSTOM, "SHOT_DIR", ix.api.OfAttr.TYPE_STRING)
projectVar = ix.application.get_factory().get_vars().add(ix.api.OfVars.TYPE_CUSTOM, "PROJECT", ix.api.OfAttr.TYPE_STRING)
episodeVar = ix.application.get_factory().get_vars().add(ix.api.OfVars.TYPE_CUSTOM, "EPISODE", ix.api.OfAttr.TYPE_STRING)
sequenceVar = ix.application.get_factory().get_vars().add(ix.api.OfVars.TYPE_CUSTOM, "SEQUENCE", ix.api.OfAttr.TYPE_STRING)
shotVar = ix.application.get_factory().get_vars().add(ix.api.OfVars.TYPE_CUSTOM, "SHOT", ix.api.OfAttr.TYPE_STRING)

#set paths for var

#set task var
taskName = task_list.get_selected_item_name()
taskVar = ix.application.get_factory().get_vars().add(ix.api.OfVars.TYPE_CUSTOM, "TASK", ix.api.OfAttr.TYPE_STRING)

return taskName, shotName, sequenceName, episodeName, projectName, directory

def saveFileAs(self, sender, evtid):

import glob

taskName, shotName, sequenceName, episodeName, projectName, filepath, userName = getSetVars(self, sender, evtid)

name = projectName + '_' + episodeName + '_' + sequenceName + '_' + shotName + '_' + taskName
firstSaveName = name + '_v001.project'
work_path = os.path.join(filepath, 'work', taskName, userName, 'clarisse')
savePath = work_path + '/' + firstSaveName
extension = "project"

#make directories on disk if they dont already
if not os.path.exists(work_path):

if os.path.exists(savePath):

file_name = os.path.join(work_path, name + '_[vV]*.' + extension)

elements = glob.glob(file_name)
version = 1
if elements:
match ='_v[0-9]+', os.path.basename(sorted(elements)[-1]))
if match:
ver ='_v')
if ver.isdigit() and int(ver) >= version:
version = int(ver) + 1
newVersionNum = 'v' + str(version).zfill(3)

savePath = os.path.join(work_path, name + '_' + newVersionNum + '.' + extension)


savePath = work_path + '/' + firstSaveName


def openFile(self, sender, evtid):

pathFromGui = path_txt.get_text()
taskFromGui = task_list.get_selected_item_name()
userName = ix.application.get_factory().get_vars().get("USER").get_string()

recent_location = pathFromGui + 'work/' + str(taskFromGui) + '/' + userName + '/clarisse'

app = ix.application

#propose to save the project if it's modified
reponse, filename = ix.check_need_save()
if reponse.is_yes():

if not reponse.is_cancelled():

if (recent_location == "" and app.get_recent_files("project").get_count() > 0):
recent_location = app.get_recent_files("project")[0]
extensions = app.get_project_extension_name()
str_ext = "{"
for i in range(extensions.get_count()):
str_ext += extensions[i]
if (i+1) < extensions.get_count():
str_ext += ","
str_ext += "}"
filename = ix.api.GuiWidget.open_file(app, recent_location, "Open Project File...", "Known Files\t*." + str_ext)
if filename != "":
clarisse_win = app.get_event_window()
old_cursor = clarisse_win.get_mouse_cursor()

def openShotFolder(self, sender, evtid):

import platform
import subprocess

taskName, shotName, sequenceName, episodeName, projectName, filepath, userName = getSetVars(self, sender, evtid)

if platform.system() == "Windows":
elif platform.system() == "Darwin":
subprocess.Popen(["open", filepath])
subprocess.Popen(["xdg-open", filepath])

def cancel(self, sender, evtid):



#Window creation
clarisse_win = ix.application.get_event_window()
window = ix.api.GuiWindow(clarisse_win, 600, 400, 590, 170)
window.set_title('Clarisse File Manager')

#Main widget creation
panel = ix.api.GuiPanel(window, 0, 0, window.get_width(), window.get_height())
panel.set_constraints(ix.api.GuiWidget.CONSTRAINT_LEFT, ix.api.GuiWidget.CONSTRAINT_TOP, ix.api.GuiWidget.CONSTRAINT_RIGHT, ix.api.GuiWidget.CONSTRAINT_BOTTOM)

#Form generation
separator_label1 = ix.api.GuiLabel(panel, 20, 10, 220, 25, "[BROWSE TO SHOT DIR]")
separator_label1.set_text_color(ix.api.GMathVec3uc(128, 128, 128))
path_button = ix.api.GuiPushButton(panel, 345, 40, 60, 22, "Browse")
path_txt = ix.api.GuiLineEdit(panel, 20, 40, 320, 22)

#check if variable already exists, if so, fill the path_txt with it
if ix.application.get_factory().get_vars().get("SHOT_DIR") is None:
pathPrintOut = "No context set, browse path -->"
pathPrintOut = ix.application.get_factory().get_vars().get("SHOT_DIR").get_string()


separator_label2 = ix.api.GuiLabel(panel, 450, 10, 180, 25, "[CHOOSE TASK]")
separator_label2.set_text_color(ix.api.GMathVec3uc(128, 128, 128))
task_list = ix.api.GuiListButton(panel, 450, 40, 125, 22)

#check if task variable already exists, if so, specify the number and use it as default
if ix.application.get_factory().get_vars().get("TASK") is None:
defaultTask = 0
if ix.application.get_factory().get_vars().get("TASK").get_string() == "light":
defaultTask = 0
if ix.application.get_factory().get_vars().get("TASK").get_string() == "lookdev":
defaultTask = 1
if ix.application.get_factory().get_vars().get("TASK").get_string() == "previs":
defaultTask = 2

#list tasks in list
for task in TASKS:

saveFile = ix.api.GuiPushButton(panel, 20, 75, 275, 25, "~~ SAVE / VERSION UP ~~")

openFile = ix.api.GuiPushButton(panel, 300, 75, 275, 25, "~~ OPEN ~~")

seperator1 = ix.api.GuiLabel(panel, 20, 100, 555, 25, "----------------------------\

cancelBtn = ix.api.GuiPushButton(panel, 300, 125, 275, 25, "Exit")

shotFolder = ix.api.GuiPushButton(panel, 20, 125, 275, 25, "Open Shot Folder")

#Connect to function
event_rewire = EventRewire() #init the class
event_rewire.connect(path_button, 'EVT_ID_PUSH_BUTTON_CLICK', event_rewire.getShotDir)
event_rewire.connect(saveFile, 'EVT_ID_PUSH_BUTTON_CLICK', event_rewire.setShotDir)
event_rewire.connect(saveFile, 'EVT_ID_PUSH_BUTTON_CLICK', event_rewire.saveFileAs)
event_rewire.connect(openFile, 'EVT_ID_PUSH_BUTTON_CLICK', event_rewire.openFile)
event_rewire.connect(cancelBtn, 'EVT_ID_PUSH_BUTTON_CLICK', event_rewire.cancel)
event_rewire.connect(shotFolder, 'EVT_ID_PUSH_BUTTON_CLICK', event_rewire.openShotFolder)

#generate the gui
while window.is_shown():
Posts: 98
Joined: Wed Aug 29, 2018 11:50 am

Re: Automated File Management Tool

Unread postby dboude » Mon Jan 06, 2020 9:23 am

Thanks for sharing that and Happy New Year !

Technical Artist - Clarisse Specialist
User avatar
Posts: 1603
Joined: Mon Jul 03, 2017 10:51 am

Return to Scripting