Как получить дерево объектов относительно указанного объекта ?

Автор Doom2, 14.08.25, 14:40:33

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

Doom2

Добрый день!


сам код (в этом коде в данный момент рассматриваю api другого САПРа и с Компасом тоже самое происходит):

from win32com.client import Dispatch, gencache
import inspect
 
def filtr_obj(obj):
    """Возвращает список имён COM-объектов, в которые можно зайти."""
    type_obj = str(type(obj))
    if "win32com" in type_obj and not inspect.isbuiltin(obj) and not inspect.isfunction(obj):
        return [name for name in dir(obj) if not name.startswith("_") and not name.endswith("_")]
    return []
 
def explore(obj, path, depth, max_depth, results, visited):
    """Рекурсивно обходит дерево объектов COM API."""
    if depth > max_depth:
        return
 
    for name in filtr_obj(obj):
        full_path = f"{path}.{name}" if path else name
 
        try:
            value = getattr(obj, name)
        except Exception:
            # Если не удаётся получить атрибут — просто записываем путь и идём дальше
            results.append(full_path)
            continue
 
        results.append(full_path)
 
        # Чтобы избежать зацикливания (одни и те же объекты в разных местах)
        if id(value) not in visited:
            visited.add(id(value))
            explore(value, full_path, depth + 1, max_depth, results, visited)
 
# ------------------- Запуск -------------------
AcadAPI = gencache.EnsureModule('{5B3245BE-661C-4324-BB55-3AD94EBBFDD7}', 0, 1, 0)
AcadObject = Dispatch('AutoCAD.Application.21', None, AcadAPI.IAcadApplication.CLSID)
 
results = []
visited = set()
explore(AcadObject, AcadObject.__class__.__name__, 1, max_depth=30, results=results, visited=visited)
 
with open("acad_object_tree.txt", "w", encoding="utf-8") as f:
    f.write("\n".join(results))
 
print(f"Объектов собрано: {len(results)}")
 

столкнулся с проблемой зацикливания - вот пример:

IAcadApplication.ActiveDocument.ActiveDimStyle.Application.ActiveDocument.ActiveDimStyle.Application.ActiveDocument.Application. и так далее

в коде можно понять что идентификаторы объектов:
IAcadApplication.ActiveDocument
и
IAcadApplication.ActiveDocument.ActiveDimStyle.Application.ActiveDocument
разные! Не пойму почему так выходит. в общем не как не могу выйти из замкнутого круга..

Doom2

Собрал список но очень долго если делать без фильтров, добавил фильтры и все быстро собирается:

from win32com.client import Dispatch, gencache
import inspect
import os

# -----------------------------------------------------------------
result_list = []
full_path_list = []
flag = True


# -----------------------------------------------------------------
def filtr_obj(obj, line_list):
    tmp = []
    type_obj = str(type(obj))
    if "win32com" in type_obj and not inspect.isbuiltin(obj) and not inspect.isfunction(obj):
        for iName in dir(obj):
            if not iName.startswith("_") and not iName.endswith("_"):
                if iName not in line_list:
                    tmp.append(iName)
    return tmp

# -----------------------------------------------------------------
def get_obj(start_obj, list_name, v):
    # получаем имя конечного элемента
    deleting = list_name[-1]
   
    # избаавляемся от дублирования объектов
    if deleting in ['ActiveDimStyle','Application', 'ActiveLayer', 'Database', 'ActiveLayout']:
        return None
   
    try:
        obj = start_obj
       
        if v == 0:
       
            for name in list_name[1:]:
                obj = getattr(obj, name)
        else:
           
            for name in list_name:
                obj = getattr(obj, name)
       
       
        return obj
    except:
        return None

# -----------------------------------------------------------------
def resize(list_objs, j, new_list_objs):
    return (
        list_objs[:j] +
        [list_objs[j] + [m] for m in new_list_objs] +
        list_objs[j+1:]
    )

# -----------------------------------------------------------------
def end_line(full_path_list, j):
    global result_list
    global flag
    result_list.append(full_path_list[j])
    del full_path_list[j]
    if not full_path_list:
        flag = False

# -----------------------------------------------------------------
def is_end(list_objs):
    return all(item[-1] == '_end' for item in list_objs)

# -----------------------------------------------------------------
def append_list2d_to_file(full_path_list, filename="output.txt", encoding="utf-8"):
   
    with open(filename, "a", encoding=encoding) as fp:
       
        for item in full_path_list:
           
            # Соединяем элементы пути через точку
            line = ".".join(item)
   
            fp.write("%s\n" % line)



# -----------------------------------------------------------------
def explore2(start_obj):
    global result_list
    global flag
    global full_path_list
   
   
    lvl = 0

    while flag:
        print(lvl)

        if os.path.isfile('./text.txt'):
            flag = False     
       
        if lvl == 0:
            for name_obj in filtr_obj(start_obj, []):
                full_path_list.append([start_obj.__class__.__name__, name_obj])
        else:
           
           
            for j, line in enumerate(full_path_list):

                obj_1 = get_obj(start_obj, line, 0)
                if obj_1:
                    new_objs = filtr_obj(obj_1, line)
                    if new_objs:
                        full_path_list = resize(full_path_list, j, new_objs)
                        break  # перезапускаем с начала
                    else:
                        end_line(full_path_list, j)
                        break
                else:
                    end_line(full_path_list, j)
                    break
        lvl += 1
       

    return 1

# ------------------- Запуск -------------------
AcadAPI = gencache.EnsureModule('{5B3245BE-661C-4324-BB55-3AD94EBBFDD7}', 0, 1, 0)
AcadObject = Dispatch('AutoCAD.Application.21', None, AcadAPI.IAcadApplication.CLSID)

# obj = get_obj(AcadObject, ['ActiveDocument','ActiveViewport'], 1)
# print(dir(obj))

if obj:
    explore2(obj)

with open("acad_object.txt", 'w', encoding="utf-8") as fp:
    for item in result_list:
        s = ".".join(x for x in item if x != '_end')
        fp.write(s + "\n")

print(f"Объектов собрано: {len(result_list)}")