Hello i’m making an automatic clustering addon for blender
i use a new method from reg/unreg found on the stack exchange at the end because of an EnumProperty class example needed.
and it is causing me an error half of the time when enabling the addon. I really don’t understand why this is happening, this is out of my league and really annoying.
the error message is here below
this is the reg/unreg code
classes = (
Scatter_Operator_Dropdown,
Scatter_PT_ScatteringPanel
)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.Scene.my_tool = PointerProperty(type=Scatter_Operator_Dropdown)
bpy.utils.register_class(ScatterPref)
bpy.utils.register_class(Scatter_OT_Custom01)
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
del bpy.types.Scene.my_tool
bpy.utils.unregister_class(ScatterPref)
bpy.utils.unregister_class(Scatter_OT_Custom01)
del bpy.types.Scene.my_tool
if __name__ == "__main__":
register()
this is the entire addon script + .py file
Scatter_v01.py (29.6 KB)
# "Scatter" Add-on
# Copyright (C) 2019 Dorian Borremans aka BD3D
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# <pep8 compliant>
bl_info = {
"name" : "Scatter [BD3D]",
"author" : "BD3D",
"description" : "The scattering tool of 2.8",
"blender" : (2, 80, 0),
"location" : "Operator",
"warning" : "",
"category" : "Generic"
}
import bpy
import bmesh
import random
from bpy.types import Menu, Panel, Operator, PropertyGroup
import webbrowser
from bpy.types import Operator, AddonPreferences, PropertyGroup
from bpy.props import StringProperty, IntProperty, BoolProperty, FloatProperty, EnumProperty, PointerProperty
import rna_keymap_ui
context = bpy.context
def find_collection(context, item):
collections = item.users_collection
if len(collections) > 0:
return collections[0]
return context.scene.collection
def make_collection(collection_name, parent_collection):
if collection_name in bpy.data.collections:
return bpy.data.collections[collection_name]
else:
new_collection = bpy.data.collections.new(collection_name)
parent_collection.children.link(new_collection)
return new_collection
def ShowMessageBox(message = "", title = "Message Box", icon = 'INFO'): #Message function
def draw(self, context):
self.layout.label(text=message)
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
######################################################################################
######################################################################################
# # # # # # # # # # # # SCATTER ADDON PREF # # # # # # # # # # # #
######################################################################################
######################################################################################
class ScatterPref(AddonPreferences):
bl_idname = __name__
addon_prefs = context.preferences.addons[__name__].preferences
scatter_01_name = StringProperty(
name="Custom slot name",
subtype='NONE',
default="Custom01"
)
scatter_01_type= bpy.props.EnumProperty(
name = 'Particle Type',
description = 'particle rendering type',
items = [
('Collection', 'Collection', '', 'COLLECTION_NEW',1),
('Object', 'Object', '', 'OBJECT_DATA',2),
],
default = 'Object')
scatter_01_count = IntProperty(
name="Emission Number",
subtype='NONE',
default=1000,
min=0,
)#percentage couvert?
scatter_01_countispersquare = BoolProperty(
name="Emission Number is per square meters (be sure to apply the scale of the terrain)",
subtype='NONE',
default=True,
)
scatter_01_countpersquare = IntProperty(
name="Emission Number per square meters",
subtype='NONE',
default=50,
min=0,
)
scatter_01_seed = IntProperty(
name="Emission Seed Value",
subtype='NONE',
default=5,
min=0,
)
scatter_01_seed_is_random = BoolProperty(
name="Emission Random Seed Value",
subtype='NONE',
default=True,
)
scatter_01_particle_size = FloatProperty(
name="Render Scale (Default = True size) ",
subtype='NONE',
default=0.25,
min=0.01,
max=3
)
scatter_01_size_random = FloatProperty(
name="Render Scale Randomness",
subtype='NONE',
default=0.35,
min=0,
max=1
)
scatter_01_phase_factor_random = FloatProperty(
name="Rotation Randomize Phase",
subtype='NONE',
default=2,
min=0,
max=2
)
scatter_01_display_percentage = IntProperty(
name="Viewport Display Percentage",
subtype='NONE',
default=100,
min=0,
max=100
)
scatter_01_noise_scale = FloatProperty(
name="Texture Noise Size",
subtype='NONE',
default=1.5,
min=0.01,
max=100
)
scatter_01_noise_scaleisrandom = BoolProperty(
name="Texture Noise Size Random Values",
subtype='NONE',
default=False,
)
scatter_01_noise_scaleA = FloatProperty(
name="Texture Noise Size Possibilities Range From A",
subtype='NONE',
default=0.75,
min=0.01,
max=100
)
scatter_01_noise_scaleB = FloatProperty(
name="To B",
subtype='NONE',
default=2.3,
min=0.01,
max=100
)
scatter_01_noise_randomtext = BoolProperty(
name="Texture Depth/Contrast/Brightness Randomisation",
subtype='NONE',
default=False,
)
scatter_01_noise_depth = IntProperty(
name="Texture Noise Depth",
subtype='NONE',
default=0,
min=0,
max=20
)
scatter_01_contrast = FloatProperty(
name="Texture Color Contrast",
subtype='NONE',
default=3,
min=0,
max=5
)
scatter_01_intensity = FloatProperty(
name="Texture Color Brightness",
subtype='NONE',
default=1,
min=0,
max=2
)
scatter_01_density_factor = FloatProperty(
name="Texture Influence Density",
subtype='NONE',
default=1,
min=0,
max=1
)
scatter_01_length_factor = FloatProperty(
name="Texture Influence Length",
subtype='NONE',
default=0.3,
min=0,
max=1
)
scatter_01_scalex = FloatProperty(
name="Texture Mapping size X",
subtype='NONE',
default=1,
min=-100,
max=100
)
scatter_01_scaley = FloatProperty(
name="Texture Mapping size Y",
subtype='NONE',
default=1,
min=-100,
max=100
)
scatter_01_scalez = FloatProperty(
name="Texture Mapping size Z",
subtype='NONE',
default=1,
min=-100,
max=100
)
scatter_01_offsetx = FloatProperty(
name="Texture Mapping offset X in meters",
subtype='NONE',
default=0,
min=-10,
max=10
)
scatter_01_offsety = FloatProperty(
name="Texture Mapping offset Y in meters",
subtype='NONE',
default=0,
min=-10,
max=10
)
scatter_01_offsetz = FloatProperty(
name="Texture Mapping offset Z in meters",
subtype='NONE',
default=0,
min=-10,
max=10
)
scatter_01_size_is_random = BoolProperty(
name="Texture Mapping Random Size Value XYZ",
subtype='NONE',
default=False,
)
scatter_01_size_A = FloatProperty(
name="Size Possibilities Range From A",
subtype='NONE',
default=0.7,
min=-100,
max=100
)
scatter_01_size_B = FloatProperty(
name="To B",
subtype='NONE',
default=1.4,
min=-100,
max=100
)
scatter_01_offset_is_random = BoolProperty(
name="Texture Mapping Random Offset Value XYZ in meters",
subtype='NONE',
default=True,
)
scatter_01_offset_A = FloatProperty(
name="Offset Possibilities Range From A",
subtype='NONE',
default=0,
min=-10,
max=10
)
scatter_01_offset_B = FloatProperty(
name="To B",
subtype='NONE',
default=10,
min=-10,
max=10
)
scatter_01_open= bpy.props.BoolProperty(
name="Show Debug Tools",
description="Expand me senpai",
default=False
)
######################################################################################
def draw(self, context): #DRAWING HERE - DRAWING HERE - DRAWING HERE- DRAWING HERE
layout = self.layout
full= "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
addon_prefs = context.preferences.addons[__name__].preferences
box = layout.box()
col = box.column()
row = col.row(align=True)
row.alignment = 'LEFT'
row.prop(self,
'scatter_01_open',
text="Custom Scattering Slot 01 ["+addon_prefs.scatter_01_name+"]",
emboss=False,
icon="PLAY" if self.scatter_01_open else "PLAY")
if self.scatter_01_open:
col.label(text=full)
col.prop(self,"scatter_01_name")
col.prop(self,"scatter_01_type")
if addon_prefs.scatter_01_type == 'Object':
col.label(text='(Will Batch-Scatter if more than one particle asset is selected)')
else:
col.label(text=' ')
col.prop(self,"scatter_01_countispersquare")
if addon_prefs.scatter_01_countispersquare == True:
col.prop(self,"scatter_01_countpersquare")
else:
col.prop(self,"scatter_01_count")
if addon_prefs.scatter_01_seed_is_random == True:
col.prop(self,"scatter_01_seed_is_random")
else:
col.prop(self,"scatter_01_seed_is_random")
col.prop(self,"scatter_01_seed")
#col.prop(self,"scatter_01_particle_size")
col.prop(self,"scatter_01_size_random")
col.prop(self,"scatter_01_phase_factor_random")
col.prop(self,"scatter_01_display_percentage")
col.label(text=' ')
col.label(text='(Set Both texture influence to 0 to turn OFF the texture clustering the particles)')
col.prop(self,"scatter_01_density_factor")
col.prop(self,"scatter_01_length_factor")
if addon_prefs.scatter_01_length_factor + addon_prefs.scatter_01_density_factor !=0:
col.prop(self,"scatter_01_offset_is_random")
if addon_prefs.scatter_01_offset_is_random == True:
col.prop(self,"scatter_01_offset_A")
col.prop(self,"scatter_01_offset_B")
else:
col.prop(self,"scatter_01_offsetx")
col.prop(self,"scatter_01_offsety")
col.prop(self,"scatter_01_offsetz")
col.prop(self,"scatter_01_size_is_random")
if addon_prefs.scatter_01_size_is_random == True:
col.prop(self,"scatter_01_size_A")
col.prop(self,"scatter_01_size_B")
else:
col.prop(self,"scatter_01_scalex")
col.prop(self,"scatter_01_scaley")
col.prop(self,"scatter_01_scalez")
col.label(text=' ')
col.prop(self,"scatter_01_noise_scaleisrandom")
if addon_prefs.scatter_01_noise_scaleisrandom == True:
col.prop(self,"scatter_01_noise_scaleA")
col.prop(self,"scatter_01_noise_scaleB")
else:
col.prop(self,"scatter_01_noise_scale")
col.prop(self,"scatter_01_noise_randomtext")
if addon_prefs.scatter_01_noise_randomtext == False:
col.prop(self,"scatter_01_noise_depth")
col.prop(self,"scatter_01_contrast")
col.prop(self,"scatter_01_intensity")
######################################################################################
######################################################################################
# # # # # # # # # # # # PROPERTIES # # # # # # # # # # # #
######################################################################################
######################################################################################
class Scatter_Operator_Dropdown(PropertyGroup):
my_enum: EnumProperty(
name="Scattering Operator Slots",
description="Choose your custom scattering slot",
items=[ ('1', "1", "", '',1),
('2', "2", "", '',2),
('3', "3", "", '',3),
('4', "4", "", '',4),
('5', "5", "", '',5),
('Auto', "Auto", "", '',6),
],
default = '1')
######################################################################################
######################################################################################
# # # # # # # # # # # # SCATTER OPERATOR # # # # # # # # # # # #
######################################################################################
######################################################################################
class Scatter_OT_Custom01(bpy.types.Operator):
bl_idname = "scatter.custom01"
bl_label = "custom scattering operator 01"
bl_description = ""
def execute(self, context):
context = bpy.context
addon_prefs = context.preferences.addons[__name__].preferences
scene = context.scene
A = context.object
bm = bmesh.new()
bm.from_mesh(A.data)
squarearea = sum(f.calc_area() for f in bm.faces)
bm.free()
if len(bpy.context.selected_objects) < 2:
ShowMessageBox("Must have at least two object selected", "Be Careful" ,"ERROR")
return {'FINISHED'}
### ### Naming
slotname = addon_prefs.scatter_01_name
if addon_prefs.scatter_01_type == 'Collection':
if len(bpy.context.selected_objects) > 2:
particlename = bpy.context.selected_objects[1].name + " ..."
else:
particlename = bpy.context.selected_objects[1].name #BUG why the f does the name change when multiple execution ???
pref = "SCATTER: ["+slotname+"] ["+particlename+"] v."
i = 1
name = "%s%d" % (pref, i)
while A.modifiers.get(name):
i += 1
name = "%s%d" % (pref, i)
### ### add particle system + rename and add selection in new coll
m = A.modifiers.new(name, type='PARTICLE_SYSTEM')
A.select_set(state=False)
for o in bpy.context.selected_objects:
o_collection = find_collection(bpy.context, o)
new_collection = make_collection(name , o_collection)
new_collection.objects.link(o)
o_collection.objects.unlink(o)
A.select_set(state=True)
### ### particle parameters
ps = m.particle_system
ps.name = name
ps.settings.name = name
ps.settings.type = 'HAIR'
ps.settings.render_type = 'COLLECTION'
ps.settings.instance_collection = bpy.data.collections[name]
ps.settings.particle_size = addon_prefs.scatter_01_particle_size
ps.settings.hair_length = 4
ps.settings.size_random = addon_prefs.scatter_01_size_random
if addon_prefs.scatter_01_countispersquare == True:
ps.settings.count = addon_prefs.scatter_01_countpersquare * squarearea
else:
ps.settings.count = addon_prefs.scatter_01_count
if addon_prefs.scatter_01_seed_is_random == True:
bpy.context.object.particle_systems[name].seed = random.randint(0,10000)
else:
bpy.context.object.particle_systems[name].seed = addon_prefs.scatter_01_seed
ps.settings.display_percentage = addon_prefs.scatter_01_display_percentage
ps.settings.use_advanced_hair = True
ps.settings.use_rotations = True
ps.settings.rotation_factor_random = 1
ps.settings.phase_factor = 1
ps.settings.phase_factor_random = addon_prefs.scatter_01_phase_factor_random
### ### texture parameters
bpy.ops.texture.new()
texturename = name
bpy.data.textures[-1].name = texturename
bpy.data.textures[texturename].type = 'CLOUDS' #LATER SUPPORT OTHER NOISE TYPE AND OPTIONS
if addon_prefs.scatter_01_noise_scaleisrandom == True:
bpy.data.textures[texturename].noise_scale = round(random.uniform(addon_prefs.scatter_01_noise_scaleA,addon_prefs.scatter_01_noise_scaleB), 2)
else:
bpy.data.textures[texturename].noise_scale = addon_prefs.scatter_01_noise_scale
if addon_prefs.scatter_01_noise_randomtext == True:
bpy.data.textures[texturename].noise_depth = random.randint(0,5)
bpy.data.textures[texturename].contrast = round(random.uniform(1.5,5),2)
bpy.data.textures[texturename].intensity = round(random.uniform(0.3,0.9),2)
else:
bpy.data.textures[texturename].noise_depth = addon_prefs.scatter_01_noise_depth
bpy.data.textures[texturename].contrast = addon_prefs.scatter_01_contrast
bpy.data.textures[texturename].intensity = addon_prefs.scatter_01_intensity
ps.settings.texture_slots.add().texture = bpy.data.textures[texturename]
ps.settings.texture_slots[0].blend_type = 'MULTIPLY'
ps.settings.texture_slots[0].use_map_time = False
ps.settings.texture_slots[0].use_map_density = True
ps.settings.texture_slots[0].density_factor = addon_prefs.scatter_01_density_factor
ps.settings.texture_slots[0].use_map_length = True
ps.settings.texture_slots[0].length_factor = addon_prefs.scatter_01_length_factor
ps.settings.texture_slots[0].texture_coords = 'GLOBAL'
if addon_prefs.scatter_01_size_is_random == False:
ps.settings.texture_slots[0].scale[1] = addon_prefs.scatter_01_scalex
ps.settings.texture_slots[0].scale[2] = addon_prefs.scatter_01_scaley
ps.settings.texture_slots[0].scale[0] = addon_prefs.scatter_01_scalez
else:
randomn =round(random.uniform(addon_prefs.scatter_01_size_A,addon_prefs.scatter_01_size_B), 2)
ps.settings.texture_slots[0].scale[1] = randomn
ps.settings.texture_slots[0].scale[2] = randomn
ps.settings.texture_slots[0].scale[0] = randomn
if addon_prefs.scatter_01_offset_is_random == False:
ps.settings.texture_slots[0].offset[1] = addon_prefs.scatter_01_offsetx
ps.settings.texture_slots[0].offset[2] = addon_prefs.scatter_01_offsety
ps.settings.texture_slots[0].offset[0] = addon_prefs.scatter_01_offsetz
else:
randomno =round(random.uniform(addon_prefs.scatter_01_offset_A,addon_prefs.scatter_01_offset_B), 2)
ps.settings.texture_slots[0].offset[1] = randomno
ps.settings.texture_slots[0].offset[2] = randomno
ps.settings.texture_slots[0].offset[0] = randomno
return {'FINISHED'}
else:
A.select_set(state=False)
for ob in bpy.context.selected_objects:
particlename = ob.name
pref = "SCATTER: ["+slotname+"] ["+particlename+"] v."
i = 1
name = "%s%d" % (pref, i)
while A.modifiers.get(name):
i += 1
name = "%s%d" % (pref, i)
### ### add particle system + rename and add selection in new coll
m = A.modifiers.new(name, type='PARTICLE_SYSTEM')
ps = m.particle_system
ps.name = name
ps.settings.name = name
ps.settings.type = 'HAIR'
ps.settings.render_type = 'OBJECT'
ps.settings.instance_object = bpy.data.objects[ob.name]
ps.settings.particle_size = addon_prefs.scatter_01_particle_size
ps.settings.hair_length = 4
ps.settings.size_random = addon_prefs.scatter_01_size_random
if addon_prefs.scatter_01_countispersquare == True:
ps.settings.count = addon_prefs.scatter_01_countpersquare * squarearea
else:
ps.settings.count = addon_prefs.scatter_01_count
if addon_prefs.scatter_01_seed_is_random == True:
bpy.context.object.particle_systems[name].seed = random.randint(0,10000)
else:
bpy.context.object.particle_systems[name].seed = addon_prefs.scatter_01_seed
ps.settings.display_percentage = addon_prefs.scatter_01_display_percentage
ps.settings.use_advanced_hair = True
ps.settings.use_rotations = True
ps.settings.rotation_factor_random = 1
ps.settings.phase_factor = 1
ps.settings.phase_factor_random = addon_prefs.scatter_01_phase_factor_random
### ### texture parameters
bpy.ops.texture.new()
texturename = name
bpy.data.textures[-1].name = texturename
bpy.data.textures[texturename].type = 'CLOUDS' #LATER SUPPORT OTHER NOISE TYPE AND OPTIONS
if addon_prefs.scatter_01_noise_scaleisrandom == True:
bpy.data.textures[texturename].noise_scale = round(random.uniform(addon_prefs.scatter_01_noise_scaleA,addon_prefs.scatter_01_noise_scaleB), 2)
else:
bpy.data.textures[texturename].noise_scale = addon_prefs.scatter_01_noise_scale
if addon_prefs.scatter_01_noise_randomtext == True:
bpy.data.textures[texturename].noise_depth = random.randint(0,5)
bpy.data.textures[texturename].contrast = round(random.uniform(1.5,5),2)
bpy.data.textures[texturename].intensity = round(random.uniform(0.3,0.9),2)
else:
bpy.data.textures[texturename].noise_depth = addon_prefs.scatter_01_noise_depth
bpy.data.textures[texturename].contrast = addon_prefs.scatter_01_contrast
bpy.data.textures[texturename].intensity = addon_prefs.scatter_01_intensity
ps.settings.texture_slots.add().texture = bpy.data.textures[texturename]
ps.settings.texture_slots[0].blend_type = 'MULTIPLY'
ps.settings.texture_slots[0].use_map_time = False
ps.settings.texture_slots[0].use_map_density = True
ps.settings.texture_slots[0].density_factor = addon_prefs.scatter_01_density_factor
ps.settings.texture_slots[0].use_map_length = True
ps.settings.texture_slots[0].length_factor = addon_prefs.scatter_01_length_factor
ps.settings.texture_slots[0].texture_coords = 'GLOBAL'
if addon_prefs.scatter_01_size_is_random == False:
ps.settings.texture_slots[0].scale[1] = addon_prefs.scatter_01_scalex
ps.settings.texture_slots[0].scale[2] = addon_prefs.scatter_01_scaley
ps.settings.texture_slots[0].scale[0] = addon_prefs.scatter_01_scalez
else:
randomn =round(random.uniform(addon_prefs.scatter_01_size_A,addon_prefs.scatter_01_size_B), 2)
ps.settings.texture_slots[0].scale[1] = randomn
ps.settings.texture_slots[0].scale[2] = randomn
ps.settings.texture_slots[0].scale[0] = randomn
if addon_prefs.scatter_01_offset_is_random == False:
ps.settings.texture_slots[0].offset[1] = addon_prefs.scatter_01_offsetx
ps.settings.texture_slots[0].offset[2] = addon_prefs.scatter_01_offsety
ps.settings.texture_slots[0].offset[0] = addon_prefs.scatter_01_offsetz
else:
randomno =round(random.uniform(addon_prefs.scatter_01_offset_A,addon_prefs.scatter_01_offset_B), 2)
ps.settings.texture_slots[0].offset[1] = randomno
ps.settings.texture_slots[0].offset[2] = randomno
ps.settings.texture_slots[0].offset[0] = randomno
A.select_set(state=True)
return {'FINISHED'}
return {'FINISHED'}
######################################################################################
######################################################################################
# # # # # # # # # # # # N MENU # # # # # # # # # # # #
######################################################################################
######################################################################################
class Scatter_PT_ScatteringPanel(bpy.types.Panel):
bl_idname = "Scatter_PT_ScatteringPanel"
bl_label = "Scatter"
bl_category = "Scatter"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_context = "objectmode"
@classmethod
def poll(self,context):
return context.object is not None
def draw(self, context):
addon_prefs = context.preferences.addons[__name__].preferences
layout = self.layout
scene = context.scene
mytool = scene.my_tool
box=layout.box()
label=box.column()
label.label(text='Select your Scattering Custom Slot')
row = box.row(align=False)
row.prop(mytool, "my_enum", text="test",expand=True)
label=box.column()
if bpy.context.scene.my_tool.my_enum =='1':
buttontext= addon_prefs.scatter_01_name
elif bpy.context.scene.my_tool.my_enum =='2':
buttontext= "test"
elif bpy.context.scene.my_tool.my_enum =='3':
buttontext= "test"
elif bpy.context.scene.my_tool.my_enum =='4':
buttontext= "test"
elif bpy.context.scene.my_tool.my_enum =='5':
buttontext= "test"
elif bpy.context.scene.my_tool.my_enum =='Auto':
buttontext= "test"
label.label(text="Start Scattering With: ["+buttontext+"]")
bigbutton = box.column()
bigbutton.scale_y=2.2
bigbutton.operator('scatter.custom01', text="SCATTER", icon='PLAY')
######################################################################################
######################################################################################
# # # # # # # # # # # # REG # # # # # # # # # # # #
######################################################################################
######################################################################################
classes = (
Scatter_Operator_Dropdown,
Scatter_PT_ScatteringPanel
)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.Scene.my_tool = PointerProperty(type=Scatter_Operator_Dropdown)
bpy.utils.register_class(ScatterPref)
bpy.utils.register_class(Scatter_OT_Custom01)
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
del bpy.types.Scene.my_tool
bpy.utils.unregister_class(ScatterPref)
bpy.utils.unregister_class(Scatter_OT_Custom01)
del bpy.types.Scene.my_tool
if __name__ == "__main__":
register()
can someone show me and others who may encounter this problem how to solve this registery issue ? thanks
Dorian