In heritance and class passing attributes?

some inheritance/attributes passing problem

I’m writing a script where I need to
1.get and store the cursor location in class V1
2.do something with it in class Adattamento

I currently set it in this way (I’m a newbie, please be kind…):

class V1(bpy.types.Operator):
bl_idname = “v.1”
bl_label = “Vertice 1”
bl_options = {“UNDO”}

def execute(self,context):
	v1 = np.array(bpy.context.scene.cursor_location)
	return {'FINISHED'}

bpy.utils.register_class(V1)

class passa(V1):
pass
bpy.utils.register_class(passa)

class Adattamento(bpy.types.Operator):
bl_idname = “adattamento.scan”
bl_label = “Adattamento”
bl_options = {“UNDO”}

def execute(self,context):
	print(V1.v1)
	return {'FINISHED'}

bpy.utils.register_class(Adattamento)

corresponding to class V1 and class Adattamento, 2 buttons have been set up.

If I excute V1 I get
bpy_class_call(): unable to get python class for rna struct ‘V_OT_1’

If I comment class “passa” I get class V1 working but obviously class “Adattamento” does not know the v1 varaible

some help?

I don’t understand your design, why is it two operators? What do you intent do achieve?

V1 is a class, and v1 is a class attribute of v1. v1 only exists within an instance of class V1. You either need that instance, or use class variables (which are basically globals). But I discourage to use class variables in conjunction with operators, you should rather use bpy.props and operator properties to store and pass data around.

Hi CoDEmanX, thank you for your reply

I need the user to place the cursor and store cursor location in a variable N times (10?). Then with those N variables I need to do some processing

To do that, I thought to create

  • N classes V1,V2…VN with their relative buttons on a panel
  • the “Adattamento” class with its button to start the processing.
    BUT I got some problems sharing variables between classes and I tried with the “passa” class

Could you please be more specific on what you would do? As I said I am not that python skilled…

Looking around the internet for some hints, I found something that looked similar to my case (here) and I wrote the code following the basic idea

class V1(bpy.types.Operator):
bl_idname = “v.1”
bl_label = “Vertice 1”
bl_options = {“UNDO”}

v1=np.array([0,0,0])

def execute(self,context):
    self.__class__.v1 = np.array(bpy.context.scene.cursor_location)
    return {'FINISHED'}

class Adattamento(bpy.types.Operator):
bl_idname = “adattamento.scan”
bl_label = “Adattamento”
bl_options = {“UNDO”}

def execute(self,context):
    print(V1.v1)
    return {'FINISHED'}

IT WORKS! but I’m curious to know why… CoDEmanX you could help, you actually wrote that post

I think I get that I just set v1 as a class variables, but I’m not sure what I made function do with self.class.v1

Maybe something like this?

import bpy
from bpy.types import Operator, Panel, UIList, PropertyGroup
from bpy.props import IntProperty, FloatVectorProperty, EnumProperty, CollectionProperty

class CursorLocations(PropertyGroup):
    co = FloatVectorProperty(
        name="Coordinate",
        description="3D location in world-space",
        size=3,
        subtype='XYZ'
    )

class WM_UL_cursor_locations(UIList):
    # The draw_item function is called for each item of the collection that is visible in the list.
    #   data is the RNA object containing the collection,
    #   item is the current drawn item of the collection,
    #   icon is the "computed" icon for the item (as an integer, because some objects like materials or textures
    #   have custom icons ID, which are not available as enum items).
    #   active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the
    #   active item of the collection).
    #   active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
    #   index is index of the current item in the collection.
    #   flt_flag is the result of the filtering process for this item.
    #   Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't
    #         need them.
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):

        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            layout.prop(item, "name", text="", emboss=False)
            layout.prop(item, "co", text="")
            
        # 'GRID' layout type should be as compact as possible (typically a single icon!).
        elif self.layout_type in {'GRID'}:
            layout.alignment = 'CENTER'
            layout.label(text="", icon_value=icon)

class WM_OT_cursor_locations(Operator):
    bl_label = "Cursor Locations"
    bl_idname = "wm.cursor_locations"
    
    action = EnumProperty(
        items=(
            ('ADD', "Add", ""),
            ('REMOVE', "Remove", ""),
            ('MOVEUP', "Move up", ""),
            ('MOVEDOWN', "Move down", ""),
            ('PRINT', "Print to system console", ""),
        )
    )
    
    def execute(self, context):
        wm = context.window_manager
        locs = wm.cursor_locations
        idx = wm.cursor_locations_index
        
        if self.action == 'ADD':
            item = locs.add()
            item.name = str(len(locs)) # random name, can be edited in UI
            item.co = context.scene.cursor_location
            wm.cursor_locations_index = len(locs) - 1
        elif self.action == 'REMOVE':
            locs.remove(idx)
            wm.cursor_locations_index = 0 if idx - 1 < 0 else idx - 1
        elif self.action == 'MOVEUP':
            locs.move(idx, (idx - 1) % len(locs))
            wm.cursor_locations_index = (idx - 1) % len(locs)
        elif self.action == 'MOVEDOWN':
            locs.move(idx, (idx + 1) % len(locs))
            wm.cursor_locations_index = (idx + 1) % len(locs)
        elif self.action == 'PRINT':
            print()
            print("========== Cursor Locations ==========")
            for loc in locs:
                print(loc.name, loc.co)
            
        return {'FINISHED'}
        

# And now we can use this list everywhere in Blender. Here is a small example panel.
class WM_PT_cursor_locations(Panel):
    """Description goes here..."""
    bl_label = "Cursor Locations"
    bl_idname = "WM_PT_cursor_locations"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    def draw(self, context):
        layout = self.layout
        wm = context.window_manager
        
        row = layout.row()
        row.template_list("WM_UL_cursor_locations", "", wm, "cursor_locations", wm, "cursor_locations_index")
        
        col = row.column(align=True)
        col.operator("wm.cursor_locations", text="", icon="ZOOMIN").action = 'ADD'
        col.operator("wm.cursor_locations", text="", icon="ZOOMOUT").action = 'REMOVE'
        #col.menu("", text="", icon="DOWNARROW_HLT")
        
        col.separator()
        col.operator("wm.cursor_locations", text='', icon='TRIA_UP').action = 'MOVEUP'
        col.operator("wm.cursor_locations", text='', icon='TRIA_DOWN').action = 'MOVEDOWN'

        layout.operator("wm.cursor_locations", text="Print to system console").action = 'PRINT'

def register():
    bpy.utils.register_module(__name__)
    bpy.types.WindowManager.cursor_locations = CollectionProperty(type=CursorLocations)
    bpy.types.WindowManager.cursor_locations_index = IntProperty()


def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.types.WindowManager.cursor_locations
    del bpy.types.WindowManager.cursor_locations_index

if __name__ == "__main__":
    register()


It’s very nice and I really appreciate your work, even if I don’t get the 100% of it… maybe the 40%… thanks
I would like to ask you a lot of things about the code, but I won’t bother you.

for comments, impressions and above all suggestions, here is what is going on:

as I said it would be a part of a larger script: with this part I should collect coords of 8 points selected by user clicks. So I need a 8x3 matrix (“Controllo”) to be passed to another class for furher processing (“Adattamento” was a draft of it used to check the class sharing of the variables).
In order to get that I have been making some modifications to get the 8x3 matrix called “Controllo” in the WM_OT_cursor_locations:

  1. inizialization as numpy.zeros((8,3))
  2. coords set added to the corresponding row as the + is pressed
  3. coords set removed from the corresponding row as the - is pressed
class WM_OT_cursor_locations(Operator):	bl_label = "Cursor Locations"
	bl_idname = "wm.cursor_locations"
    
	action = EnumProperty(
		items=(
			('ADD', "Add", ""),
			('REMOVE', "Remove", ""),
			('MOVEUP', "Move up", ""),
			('MOVEDOWN', "Move down", ""),
			('PRINT', "Print to system console", ""),
		)
	)


	controllo = np.zeros((8,3))
	
	def execute(self, context):
		wm = context.window_manager
		locs = wm.cursor_locations
		idx = wm.cursor_locations_index


		
		if self.action == 'ADD':
			item = locs.add()
			item.name = str(len(locs)) # random name, can be edited in UI
			item.co = context.scene.cursor_location
			cidx=int(item.name)
			self.__class__.controllo[cidx-1,:] = np.round(np.array(item.co), decimals=4)
			wm.cursor_locations_index = len(locs) - 1
			print(cidx, self.__class__.controllo)
		elif self.action == 'REMOVE':
			locs.remove(idx)
			cidx = len(locs)+1
			self.__class__.controllo[cidx-1,:] = np.array([0,0,0])
			print(cidx, self.controllo)
			wm.cursor_locations_index = 0 if idx - 1 < 0 else idx - 1
		elif self.action == 'MOVEUP':
			locs.move(idx, (idx - 1) % len(locs))
			wm.cursor_locations_index = (idx - 1) % len(locs)
		elif self.action == 'MOVEDOWN':
			locs.move(idx, (idx + 1) % len(locs))
			wm.cursor_locations_index = (idx + 1) % len(locs)
		elif self.action == 'PRINT':
			print()
			print("========== Cursor Locations ==========")
			for loc in locs:
				print(loc.name, loc.co)
			
		return {'FINISHED'}

I set “controllo” as self.class.controllo because I noticed it will be passed to other classes (even if I don’t quite know why, yet). There are some print() here and there to check what is going on

a little TODO list for me

  • add a “CLEAR EVERYTHING” feature for the collected coords: not so easy to do (at least for me)
  • add a check and a feedback error message: almost easy to do
  • the order selection is a constraint for the user, I’m going to remove the feature: easy to do

any suggestion is welcome

numpy is already called before in the script for a lot of matricial/algebraic stuff (I’m a MATLAB guy…)

how about this for the cidx index? the idx index doesn0t work as I expected: it starts with 0, at the first selection sticks with 0 and only from the second selection it grows adding 1. Also when coming back removing selections, at the first action it doesn’t go -1 but only from the second action.
I didn’t find anything about context.window_manager.cursor_locations_index in the documentation

I also implemented a check+correction for wrong selection. I’m going to look something more visual for the user then the print() when he get the selection wrong.

def execute(self, context):		wm = context.window_manager
		locs = wm.cursor_locations
		idx = wm.cursor_locations_index
		#determinazione limiti coord dello scan per check della selezione
		obj = bpy.context.active_object
		vscancoo = [(obj.matrix_world * v.co) for v in obj.data.vertices] # coordinate vertici nel rif globale (matrix world)
		vscancoo = np.around(np.array(vscancoo), decimals=4)
		Xmax=np.max(vscancoo[:,0])
		Xmin=np.min(vscancoo[:,0])
		Ymax=np.max(vscancoo[:,1])
		Ymin=np.min(vscancoo[:,1])
		Zmax=np.max(vscancoo[:,2])
		Zmin=np.min(vscancoo[:,2])


		
		if self.action == 'ADD':
			item = locs.add()
			item.name = str(len(locs)) # random name, can be edited in UI
			item.co = context.scene.cursor_location
			#item.co =obj.matrix_world * item.co # coordinate nel rif globale (matrix world)
			cidx=len(locs)
			self.__class__.controllo[cidx-1,:] = np.round(np.array(item.co), decimals=4)
			wm.cursor_locations_index = len(locs) - 1
				# check della selezione
			if (np.max(item.co[0])>Xmax) or (np.min(item.co[0])<Xmin) or (np.max(item.co[1])>Ymax) or (np.min(item.co[1])<Ymin) or (np.max(item.co[2])>Zmax) or (np.min(item.co[2])<Zmin):
				print('SELEZIONE FUORI DELLO SCAN, EFFETTUARE NUOVA SELEZIONE')
				locs.remove(idx)
				cidx = len(locs)
				self.__class__.controllo[cidx,:] = np.array([0,0,0])
				wm.cursor_locations_index = 0 if idx - 1 < 0 else idx - 1
			else:
				print('SELEZIONE CORRETTA')
				#
		elif self.action == 'REMOVE':
			locs.remove(idx)
			cidx = len(locs)
			self.__class__.controllo[cidx,:] = np.array([0,0,0])
			wm.cursor_locations_index = 0 if idx - 1 < 0 else idx - 1
		return {'FINISHED'}

what I actually don’t understand it is how it gets the names of the selections in the print box of the panel (1,2,3…), plus they do not remain sequential if the next selected item is wrong (with respect to the check conditions)

I’m going to have a look to the linked script: I need some time to study it…

No, the user is not supposed to change the selection order

cursor_locations_index is just an IntProperty() I defined to store the selection of the template_list().


		vscancoo = [(obj.matrix_world * v.co) for v in obj.data.vertices] # coordinate vertici nel rif globale (matrix world)
		vscancoo = np.around(np.array(vscancoo), decimals=4)
		Xmax=np.max(vscancoo[:,0])
		Xmin=np.min(vscancoo[:,0])
		Ymax=np.max(vscancoo[:,1])
		Ymin=np.min(vscancoo[:,1])
		Zmax=np.max(vscancoo[:,2])
		Zmin=np.min(vscancoo[:,2])

Does this calculate the axis-aligned bounding box of the active object?

What does this do? Check for overlaps based on bounding box?


			if (np.max(item.co[0])>Xmax) or (np.min(item.co[0])<Xmin) or (np.max(item.co[1])>Ymax) or (np.min(item.co[1])<Ymin) or (np.max(item.co[2])>Zmax) or (np.min(item.co[2])<Zmin):

Yes, it calculates the bounding box because the selection has to be ON the active object surface (with the Enhanced 3D Cursor Tool installed for cursor snapping on object).

The following IF statement purpose is to avoid wrong selections, to be sure the user clicks on the active object

How is the bounding box helping if you want the surface? Shouldn’t you raycast the actual location on the surface?

Here’s a slightly modified version of the raycast template:

import bpy
from mathutils import Vector
from bpy_extras import view3d_utils


def main(context, event, ray_max=10000.0):
    """Run this function on left mouse, execute the ray cast"""
    # get the context arguments
    scene = context.scene
    region = context.region
    rv3d = context.region_data
    coord = event.mouse_region_x, event.mouse_region_y

    # get the ray from the viewport and mouse
    view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
    ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
    ray_target = ray_origin + (view_vector * ray_max)


    def obj_ray_cast(obj, matrix):
        """Wrapper for ray casting that moves the ray into object space"""

        # get the ray relative to the object
        matrix_inv = matrix.inverted()
        ray_origin_obj = matrix_inv * ray_origin
        ray_target_obj = matrix_inv * ray_target

        # cast the ray
        hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj)

        if face_index != -1:
            return hit, normal, face_index
        else:
            return None, None, None
    
    obj = context.object
    if obj.type == 'MESH' and obj.dupli_type == 'NONE':
        matrix = obj.matrix_world.copy()
        hit, normal, face_index = obj_ray_cast(obj, matrix)
        if hit is not None:
            hit_world = matrix * hit
            scene.cursor_location = hit_world
            #length_squared = (hit_world - ray_origin).length_squared
            length = (hit_world - ray_origin).length
            return (hit_world, length)


class ViewOperatorRayCast(bpy.types.Operator):
    """Modal object selection with a ray cast"""
    bl_idname = "view3d.modal_operator_raycast"
    bl_label = "RayCast View Operator"

    def modal(self, context, event):
        if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
            # allow navigation
            return {'PASS_THROUGH'}
        elif event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            hit_world, length = main(context, event)
            self.report({'INFO'}, "%s %.2f" % (hit_world, length))
            return {'FINISHED'}
        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        if context.space_data.type == 'VIEW_3D':
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "Active space must be a View3d")
            return {'CANCELLED'}


def register():
    bpy.utils.register_class(ViewOperatorRayCast)


def unregister():
    bpy.utils.unregister_class(ViewOperatorRayCast)


if __name__ == "__main__":
    register()


It only hits the active object and prints the actual hit location on the surface plus the travelled distance.

Here’s my solution:

import bpy
from mathutils import Vector
from bpy_extras import view3d_utils
from bpy.types import Operator, Panel, UIList, PropertyGroup
from bpy.props import IntProperty, FloatVectorProperty, EnumProperty, CollectionProperty

class CursorLocations(PropertyGroup):
    co = FloatVectorProperty(
        name="Coordinate",
        description="3D location in world-space",
        size=3,
        subtype='XYZ'
    )


def main(context, event, ray_max=10000.0):
    """Run this function on left mouse, execute the ray cast"""
    # get the context arguments
    ob = context.object
    scene = context.scene
    region = context.region
    rv3d = context.region_data
    coord = event.mouse_region_x, event.mouse_region_y

    # get the ray from the viewport and mouse
    view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
    ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
    ray_target = ray_origin + (view_vector * ray_max)


    def ob_ray_cast(ob, matrix):
        """Wrapper for ray casting that moves the ray into object space"""

        # get the ray relative to the object
        matrix_inv = matrix.inverted()
        ray_origin_ob = matrix_inv * ray_origin
        ray_target_ob = matrix_inv * ray_target

        # cast the ray
        hit, normal, face_index = ob.ray_cast(ray_origin_ob, ray_target_ob)

        if face_index != -1:
            return hit, normal, face_index
        else:
            return None, None, None
    
    if ob.type == 'MESH' and ob.dupli_type == 'NONE':
        matrix = ob.matrix_world.copy()
        hit, normal, face_index = ob_ray_cast(ob, matrix)
        if hit is not None:
            hit_world = matrix * hit
            scene.cursor_location = hit_world
            #length_squared = (hit_world - ray_origin).length_squared
            #length = (hit_world - ray_origin).length
            item = ob.cursor_locations.add()
            item.name = str(len(ob.cursor_locations))
            item.co = hit_world


class VIEW3D_OT_cursor_locations(Operator):
    """Modal object selection with a ray cast"""
    bl_idname = "view3d.cursor_locations"
    bl_label = "Cursor Locations"

    @classmethod
    def poll(cls, context):
        return (context.object is not None and
                context.object.type == 'MESH' and
                context.object.dupli_type == 'NONE')

    def modal(self, context, event):
        
        ob = context.object
        
        context.area.header_text_set("LMB to raycast, RMB to revert last, Esc to abort. %i of 8 coordinates..." % len(ob.cursor_locations))
        
        if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
            # allow navigation
            return {'PASS_THROUGH'}
        
        elif event.value == 'PRESS':
            if event.type == 'LEFTMOUSE':
                main(context, event)
                if len(ob.cursor_locations) >= 8:
                    return self.end(context)
            
            elif event.type == 'RIGHTMOUSE':
                if len(ob.cursor_locations) > 0:
                    idx = len(ob.cursor_locations) - 1
                    ob.cursor_locations.remove(idx)
                    ob.data.update()
                else:
                    return self.end(context)
            
            return {'RUNNING_MODAL'}
        
        elif event.type == 'ESC':
            return self.end(context)

        return {'RUNNING_MODAL'}
    
    def end(self, context):
        context.area.header_text_set()
        return {'FINISHED'}
    
    def invoke(self, context, event):
        if context.space_data.type == 'VIEW_3D':
            ob = context.object
            if len(ob.cursor_locations) >= 8:
                ob.cursor_locations.clear()
                ob.data.update()
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "Active space must be a View3d")
            return {'CANCELLED'}


class OBJECT_UL_cursor_locations(UIList):
    
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):

        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            layout.prop(item, "name", text="", emboss=False)
            #layout.prop(item, "co", text="")
            layout.label("%.3f" % item.co[0])
            layout.label("%.3f" % item.co[1])
            layout.label("%.3f" % item.co[2])

            
        # 'GRID' layout type should be as compact as possible (typically a single icon!).
        elif self.layout_type in {'GRID'}:
            layout.alignment = 'CENTER'
            layout.label(text="", icon_value=icon)


class OBJECT_OT_cursor_locations(Operator):
    bl_label = "Cursor Locations"
    bl_idname = "object.cursor_locations"
    
    action = EnumProperty(
        items=(
            ('ADD', "Add", ""),
            ('REMOVE', "Remove", ""),
            ('CLEAR', "Clear", ""),
            ('MOVEUP', "Move up", ""),
            ('MOVEDOWN', "Move down", ""),
            ('PRINT', "Print to system console", ""),
        )
    )
    
    poll = VIEW3D_OT_cursor_locations.poll
    
    def execute(self, context):
        ob = context.object
        locs = ob.cursor_locations
        
        if self.action == 'ADD':
            #item = locs.add()
            #item.name = str(len(locs)) # random name, can be edited in UI
            #item.co = context.scene.cursor_locations
            pass
        elif self.action == 'REMOVE':
            locs.remove(len(locs)-1)
        elif self.action == 'CLEAR':
            locs.clear()
        elif self.action == 'MOVEUP':
            pass
        elif self.action == 'MOVEDOWN':
            pass
        elif self.action == 'PRINT':
            print()
            print("========== Cursor Locations ==========")
            for loc in locs:
                print(loc.name, loc.co)
            
        return {'FINISHED'}
    

class OBJECT_PT_cursor_locations(Panel):
    """Description goes here..."""
    bl_label = "Cursor Locations"
    bl_idname = "OBJECT_PT_cursor_locations"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout
        ob = context.object
        
        row = layout.row()
        row.enabled = False
        row.template_list("OBJECT_UL_cursor_locations", "", ob, "cursor_locations", ob, "cursor_locations_index", rows=8)
        
        """
        col = row.column(align=True)
        col.operator("object.cursor_locations", text="", icon="ZOOMIN").action = 'ADD'
        col.operator("object.cursor_locations", text="", icon="ZOOMOUT").action = 'REMOVE'
        #col.menu("", text="", icon="DOWNARROW_HLT")
        
        col.separator()
        col.operator("object.cursor_locations", text='', icon='TRIA_UP').action = 'MOVEUP'
        col.operator("object.cursor_locations", text='', icon='TRIA_DOWN').action = 'MOVEDOWN'
        """
        layout.operator("object.cursor_locations", text="Clear list", icon="X").action = 'CLEAR'
        layout.operator("object.cursor_locations", text="Print to system console", icon="CONSOLE").action = 'PRINT'

def draw_func(self, context):
    self.layout.operator(VIEW3D_OT_cursor_locations.bl_idname)


def register():
    bpy.utils.register_module(__name__)
    bpy.types.Object.cursor_locations = CollectionProperty(type=CursorLocations)
    bpy.types.Object.cursor_locations_index = IntProperty(default=-1)
    bpy.types.VIEW3D_PT_tools_transform.append(draw_func)


def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.types.Object.cursor_locations
    del bpy.types.Object.cursor_locations_index
    bpy.types.VIEW3D_PT_tools_transform.remove(draw_func)

if __name__ == "__main__":
    register()


Make sure the properties editor is visible and on object tab. Run it via the “Cursor locations” button in the 3D View T-toolshelf (with mesh object selected and being in object mode). Then Click on the active object.

it’s great what you did, I think Im going to implent it after some modifications.
Soon I would like to have

  • the user to drag the cursor holding the LMB and get the selection on LMB release
  • a colored mark or something once the location is selected. It could be also a little colored cube as new object, just to have the user aware of what he did

thank you very much for you help

See the Enhanced 3D Cursor addon, it supports drag-snapping. But beware of bad performance.

You need a modal operator with a draw handler to draw in view port with OpenGL (see the included templates). Or place empties at the clicked locations.