# Python 2.8 - forcing the correct instance of the image/uv editor

BACKGROUND I have code (below) that scales a texture on the Y-axis to eliminate distortion from unwrapping a very deep cylinder.

The code performs successfully on a standalone basis (as a test), but when I drop the code into a bigger app it throws an error because setting an area to IMAGE_EDITOR does not behave the same way as in the test.

Stand alone performance: the texture image appears correctly when I set an area to IMAGE_EDITOR; from there the code scales the UV map correctly by selecting all the uv vertices and scaling.

Within big app performance: a blank appears when I set an area to IMAGE_EDITOR. The code throws an error because there are no uv vertices to select. However, the image/uvmap exists and can be found when I switch the workspace from "layout" to "uv editing"

QUESTIONS ON HOW I CAN FIX THE CODE SO THAT IT WORKS IN THE BIG APP

(a) If the correct texture image/uvmap appears in IMAGE_EDITOR, can I assume the code will work as well when included in big app as in the test?

(b) How can I force IMAGE_EDITOR to perform in the big app the same way it performs as a standalone?

SCREENSHOTS AND CODE

On a standalone basis, the code successfully resizes the UV map and eliminates the texture distortion:

import bpy

TextureFolder='G:\\Blender\\Textures\\'
TextureFilename='wildtextures-zinc-galvanized-metal-sheet.jpg'

def obj_material_texture_UVscale(objname,filename,Xscale,Yscale,Zscale):
path=TextureFolder+filename
materials = bpy.data.materials
material = materials.get('mat')
if not material:
material = materials.new('mat')
material.use_nodes = True
if material.node_tree:
material.node_tree.nodes.clear()
nodes = material.node_tree.nodes
output = nodes.new(type = 'ShaderNodeOutputMaterial' )
diffuse = nodes.new(type = 'ShaderNodeBsdfDiffuse' )
node_texture.image = imgTex
node_texture.location = 0,200
obj=bpy.data.objects[objname]
obj.active_material=material
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[objname].select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.smart_project()

original_area = bpy.context.area.type

scene = bpy.context.scene
for count, ob in enumerate(bpy.context.selected_objects):
if ob.type == 'MESH':
#scene.objects.active = ob
bpy.context.view_layer.objects.active=ob
#+ switch to the image editor to perform transforms etc
bpy.context.area.type = 'IMAGE_EDITOR'
bpy.ops.object.mode_set(mode='EDIT', toggle=False)

bpy.ops.mesh.reveal()
bpy.ops.mesh.select_all(action='SELECT')
#+ select the uvs
bpy.ops.uv.select_all(action='SELECT')

bpy.ops.transform.translate(value=(0, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.resize(value=(Xscale,Yscale,Zscale))

bpy.context.area.type = original_area
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

obj_material_texture_UVscale('Cylinder',TextureFilename,1,30,1)


You do not need to mess around with the UI or change the layout at all if all you want to do is update a piece of data within an object.

You can dig down into any variable's type interactively in the scripting console without even having to look up the documentation. Autocomplete via ctrl-space, or calling dir(obj) and type(foo) describes just about anything.

So all you need to do is dig down to find where the UV coordinates are stored, and multiply them directly.

Here's how that would look for your script.

import bpy

TextureFolder = 'G:\\Blender\\Textures\\'
TextureFilename = 'wildtextures-zinc-galvanized-metal-sheet.jpg'

def obj_material_texture_UVscale(objname, filename, Xscale, Yscale, Zscale):
path = TextureFolder+filename
materials = bpy.data.materials
material = materials.get('mat')
if not material:
material = materials.new('mat')
material.use_nodes = True
if material.node_tree:
material.node_tree.nodes.clear()
nodes = material.node_tree.nodes
output = nodes.new(type = 'ShaderNodeOutputMaterial' )
diffuse = nodes.new(type = 'ShaderNodeBsdfDiffuse' )
node_texture.image = imgTex
node_texture.location = 0,200
obj = bpy.data.objects[objname]
obj.active_material = material
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[objname].select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.smart_project()
bpy.ops.object.mode_set(mode='OBJECT')

# Just directly scale the UVs!
cyluvs = obj.data.uv_layers[0].data
for cuv in cyluvs: # each is a bpy.types.MeshUVLoop
cuv.uv[0] *= Xscale
cuv.uv[1] *= Yscale

# len(o.data.uv_layers['UVMap'].data) -> 192
# Each of these is a bpy.types.MeshUVLoop
# which has .uv, .select, .select_edge, .pin_uv.
# item.uv is a Vector. We can just scale its Y 30 times!

obj_material_texture_UVscale('Cylinder',TextureFilename,1,30,1)

• that works really well. I can't thank you enough after me going down rabbit holes for 10+ hours. – vndep Aug 25 '19 at 12:15