Real beginner in Python and code needs some help to create Scripts

Started by Jw2t, December 16, 2015, 12:40:58 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Jw2t

Hi everyone,

I think I use Keyshot rather well but I need to make hundreds similar action and I want to create a script to save considerable time. The problem is I have never create a script with Python or an other language.
My workflow ;
   1. Open a Keyshot file (no need to automatize this step)
   2. Create 3 views ; face, isometric and an another one (this step is ok, I modified a little bit the Keyshot's script)
   3. Hide 2 group of my object (my keyshot file contains many parts separated in 3 differents groups)
   4. Apply a material to the "active/not hidden" group
   5. Launch a render of this "scene" (this step is ok too)
   6. Apply an other material to the group
 
Repeat steps 4 to 6 many times, then,

   7. Hide the "active/not hidden" group and active/show one of the 2 "not activated/hidden" groups
   8. Step 4 to 6 again and again
   9. Change the active view
   10. Step 3 to 8
   Etc.

At final, it's necessary that hundreds files creates with the script have a specific name in function of the view, the material applied and the group "activated/not hidden"

I imagine that my request is very long and complex but I can't do without a script. Thank you in advance for your attention to my message.

Jerome

Ryan Fenik

Hello,

This should get you started. 

Look at "show output" for the print result. 

You'll want some conditional logic in the for loop to filter the parts you want hidden, something like "if "Red" in combination[0]:"

Also, replace the print line with something like "lux.renderImage(path = path, width = 1200, height = 800)"


# AUTHOR RF
# VERSION 0.0.1
# Generates all possible combinations and prints them out

import itertools

values = [("folder", lux.DIALOG_FOLDER, "Output folder:", None),
("fmt", lux.DIALOG_TEXT, "Output file format:", "ColorTest_%d.png"),]

opts = lux.getInputDialog(title = "Prints All Possible Combinations",
                          desc = "Where do you want the output folder?",
                          values = values,
                          id = "All_Combinations_Test.py.RF")

fmt = opts["fmt"]
folder = opts["folder"]

Colors = ["Red", "Blue", "Green", "Yellow"]
Sizes = ["Small", "Medium", "Large", "Extra Large"]
Language = ["English", "Spanish", "French"]

root = lux.getSceneTree()
for node in root.find(mat = "materialToBeReplaced"):
    node.setMaterial("New_Material")

for combination in itertools.product(Colors, Sizes, Language):
    for node in root.find(name = "partToBeHidden"):
        node.hide()
    combination_string = '_'.join(combination)

    path = os.path.join(folder, fmt.replace("%d", combination_string))
    print (path)



Jw2t

Ryanfenik,

Thank your for your answer ! With your script, I create differents files with a specifique name as I want it.
However, I have some difficulties with differents points ;
  1. I don't succeed to hide some elements
  2. I don't know how to define a material to a part
  3. I can't define a time max for these renderings
  4. I can't add my various rendering to the queue

Thank you again in advance for your patience and your support.

Best,

Jerome

Ryan Fenik

1.  Put this in the "for combination" loop to conditionally hide elements: 

    root.show() #shows everything
    if "Red" in combination[0]:
        for node in root.find("red"):
        node.hide()

2.  This will search for all materials that contain the word "Plastic" and change them to "Metal": 

root = lux.getSceneTree()
for node in root.find(mat = "Plastic"):
    node.setMaterial("Metal")

3.  To set a max rendering time, do this: 

opts = lux.getRenderOptions()
opts.setMaxTimeRendering(10)
lux.renderImage(path = path, width = 1200, height = 1000, opts = opts)

4.  Why do you need this?  Network rendering?  It'll render every time the script goes to "lux.renderImage()". 

Morten Kristensen

Hello Jerome,

In addition to what Ryan said, you can add to the render queue like this:

opts = lux.getRenderOptions()
opts.setAddToQueue(True)
lux.renderImage(path = path, width = 1200, height = 1000, opts = opts)


And processing the queue is done using lux.processQueue() or you open the queue yourself and process it there. Just be sure you don't add multiple renderings to the queue named the exact same thing.

Jw2t

Hi ryanfenik, hi Morten Kristensen,

Thank you for your answers. To answer to your question n°4 ryanfenik, I need this script to allow the customization of many products on the web. Products will composed of 3 parts and each part will be painted of a different color. Furthermore, to present customized products , 3 views will be available.

Before I added new operations, I could create differents files with a specifique name as I wanted it.
Now, I get only 3 differents results (unsatisfactory) ;
1. Several rendering, only one change of colors, no change of name, so only one file
2. Several rendering, change of names and many files
3. Bug and crash of Keyshot when I want to create many rendering with differents change of colors

Moreover, when I want to hide elements I get this error :
"  File "<string>", line 30
    node.hide()
       ^
IndentationError: expected an indented block"

Thank you again for your answer and your patience.

Jerome

Morten Kristensen

If you get errors then please give more information about them.

You need to indent that line once, Ryan's example was missing an indentation.

Jw2t

You're right Morten,
Please find below my script :

# AUTHOR RF-JW2T-MK
# VERSION 0.0.0
# (BUILDING) Generates all possible combinations and prints them out

views = {lux.VIEW_FRONT: "VIEW_1_FACE",   
         lux.VIEW_ISOMETRIC: "VIEW_2_ISO"}

lux.newCamera("VIEW_3_DETAIL")
True
lux.setCameraLookAt(pt = (1, 1, 1)) # Example manipulation.
lux.saveCamera()

for view in views:
    name = views[view]
    has = False
    if name in lux.getCameras():
        has = True
        lux.setCamera(name)
    else:
        lux.newCamera(name)
    lux.setStandardView(view)
    lux.saveCamera()
    print("{} camera: {}".format("Updated" if has else "Created", name))

values = [("folder", lux.DIALOG_FOLDER, "Output folder:", None),
("fmt", lux.DIALOG_TEXT, "Output file format:", "%d.png"),]

opts = lux.getInputDialog(title = "Prints All Possible Combinations",
                          desc = "Where do you want the output folder?",
                          values = values,
                          id = "All_Combinations_Test.py.RF")

fmt = opts["fmt"]
folder = opts["folder"]

Color = ["30", "31", "32", "33"]
Nameofproduct = ["BuffetBanzaï"]
View = ["View_1_FACE"]
Group = ["GROUP1", "GROUP2", "GROUP3"]

root = lux.getSceneTree()
for node in root.find(mat = "30"):
    node.setMaterial("32")

for combination in itertools.product(Color, Nameofproduct, View, Group):
    combination_string = '_'.join(combination)

    path = os.path.join(folder, fmt.replace("%d", combination_string))

root.show() #shows everything
if "GROUP1" in combination[0]:
for node in root.find("GROUP1"):
    node.hide()

lux.setCamera("VIEW_1_FACE")

opts = lux.getRenderOptions()
opts.setMaxTimeRendering(10)
opts.setAddToQueue(True)
lux.renderImage(path = path, width = 1200, height = 1000, opts = opts)

root = lux.getSceneTree()
for node in root.find(mat = "31"):
    node.setMaterial("33")

opts = lux.getRenderOptions()
opts.setMaxTimeRendering(10)
opts.setAddToQueue(True)
lux.renderImage(path = path, width = 1200, height = 1000, opts = opts)

lux.processQueue()

Jw2t

Hi all,
With modifications, I can;
- change colors
- create, change and activate different views
- launch many renderings

However, I always have difficulties to:
- have differents files (all my renderings have the same name and each new rendering deletes the precedent)
- hide some GROUP of object

Concerning the fact to hide or not a group, I have found a better way to get what I want ; the export on various layers. I understand how works layers but I can't to export my 3 group in 3 differents layers with this code :

opts = lux.getRenderOptions()
opts.setMaxTimeRendering(10)
opts.setAddToQueue(True)
opts.setOutputRenderLayers(Calque_GROUPE1)
lux.renderImage(path = path, width = 1200, height = 1000, opts = opts)

Does anybody have an idea to help me?
Thank you in advance,

Best,
Jerome

Morten Kristensen

Hi Jerome,

Why don't you name them something different or put them into different folders? :)

What do you mean you have difficulties hiding groups? It works fine for me. If you found a bug then please do tell.

Thanks

Jw2t

Hi Mortensen,

I don't know how to name my renderings with differents names.
After the 1st answer of ryanfenik, I managed to get many renderings with differents names but always with the same colors and the same view.
Now I succeed to change colors, views and add my renderings to the queue but when I proceed the queue, I don't find files created ; its as if files were not created even if renderings were done!

Concerning my wish to hide groups, I have studied that rendering with many layers can be better for my project. But I also block to this step.

Could you help me to find why files seems not created and how I can get layers in a script? Thank you so much for your patience, I know that I have a lot of difficulties with all of this.

# AUTHOR Jw
# VERSION 0.0.1
# TEST

values = [("folder", lux.DIALOG_FOLDER, "Output folder:", None),
("fmt", lux.DIALOG_TEXT, "Output file format:", ".png"),]

opts = lux.getInputDialog(title = "Prints All Possible Combinations",
                          desc = "Where do you want the output folder?",
                          values = values,
                          id = "All_Combinations_Test.py.RF")

fmt = opts["fmt"]
folder = opts["folder"]

views = {lux.VIEW_FRONT: "VIEW_2_FACE",   
         lux.VIEW_ISOMETRIC: "VIEW_1_ISO"}

lux.newCamera("VIEW_3_DETAIL")
True
lux.setCameraLookAt(pt = (1, 1, 1)) # Example manipulation.
lux.saveCamera()

for view in views:
    name = views[view]
    has = False
    if name in lux.getCameras():
        has = True
        lux.setCamera(name)
    else:
        lux.newCamera(name)
    lux.setStandardView(view)
    lux.saveCamera()
    print("{} camera: {}".format("Updated" if has else "Created", name))

lux.setCamera("VIEW_1_ISO")

root = lux.getSceneTree()
for node in root.find(mat = "30"):
    node.setMaterial("31")

opts = lux.getRenderOptions()
opts.setMaxTimeRendering(10)
opts.setAddToQueue(True)
opts.setOutputRenderLayers()
lux.renderImage(path = "31-Product1-VIEW_1_ISO.png", width = 1200, height = 1000, opts = opts)

lux.setCamera("VIEW_2_FACE")

root = lux.getSceneTree()
for node in root.find(mat = "32"):
    node.setMaterial("33")

opts = lux.getRenderOptions()
opts.setMaxTimeRendering(10)
opts.setAddToQueue(True)
lux.renderImage(path = "33-Product1-VIEW_2_FACE.png", width = 1200, height = 1000, opts = opts)

lux.setCamera("VIEW_3_DETAIL")

root = lux.getSceneTree()
for node in root.find(mat = "33"):
    node.setMaterial("32")

opts = lux.getRenderOptions()
opts.setMaxTimeRendering(10)
opts.setAddToQueue(True)
lux.renderImage(path = "32-Product1-VIEW_3_DETAIL.png", width = 1200, height = 1000, opts = opts)

Morten Kristensen

Hi Jerome,

The files are created but it is recommended to use absolute file paths because you know exactly where they will be placed.
So do like:
  lux.renderImage(path = "/path/to/place/the/image.png")
Of course, you have to input a correct path (for Windows you'll need the drive letter in front).

The name you put is the name of the file, that was what I meant with different names. Otherwise, you can use different folders, too.

The camera should change just fine as you do with lux.setCamera("VIEW_1_ISO") etc. You can try in the console to test changing the cameras so you can see the intermediate results.

It's no problem. :)

By the way when posting code then please either attach a .py file or link to the code somewhere else. Thanks

Jw2t

Dear Morten,

Thank you, your answer is clear and thanks to it, I get my differents files!
I have one last step on which I meet difficulties ; I wanted get .png files with elements hide but it will be better to get .psd files with differents layers.
I have found the code "setOutputRenderLayers(...)" but I don't succeed to use it. (I hope I will get a transparent scene with .psd because I always have the scene in background on my .png file).
Thank you in advance, I promise that I will stop to bother you soon.

Jw2t

Hi all,

I'm working always on my script and I met different problems ;

1. If I could launch a .psd rendering it would be wonderful (I could show all my components and launch only one rendering to get 3 components) Is it possible to do it?

2. How quickly replace a character such as '%' in the name in my many renderings without replace it in each line such as path = "Product1name-view..."?

Thank you in advance for your answer.

Regards,

Ryan Fenik

Jerome,

See the attached script, it will help you better understand  questions 1 & 4: 

• Imports a cube
• Assigns different materials to the cube
• Renders each combination to a unique name. 

If you are on a mac you will have to change the line "lux.importFile("C:/Users/Public/Documents/KeyShot 6/Models/Cube.bip") " to the appropriate file location. 

Below are images of what the script will output.