Здравствуйте.
Пытаюсь сделать свои стандартные изделия 3D. Каждый экземпляр - отдельный файл с внешним объектом спецификации.
Подскажите как добраться до табличного атрибута объекта спецификации стандартного изделия. API7
Есть деталь с единственным объектом спецификации, вставленным шаблоном.
Теперь нужно изменить параметры и сохранить под другим именем.
добрался до базового объекта, даже номер атрибута есть. А как сам атрибут-то открыть?
Вот код на питоне 3.10
# -*- coding: utf-8 -*-
import pythoncom
from win32com.client import Dispatch, gencache
# Получить интерфейсы API 7 КОМПАС
def get_kompas_api7():
module = gencache.EnsureModule("{69AC2981-37C0-4379-84FD-5DD2F3C0A520}", 0, 1, 0)
api = module.IKompasAPIObject(Dispatch("Kompas.Application.7")._oleobj_.QueryInterface(module.IKompasAPIObject.CLSID, pythoncom.IID_IDispatch))
const = gencache.EnsureModule("{75C9F5D0-B5B8-4526-8681-9903C567D2ED}", 0, 1, 0)
return module, api, const.constants
module_7, i_kompas_api_object, const_7 = get_kompas_api7()
application = i_kompas_api_object.Application
iKompasDocument = application.ActiveDocument
iSpecificationDescription = iKompasDocument.SpecificationDescriptions.Active
BaseObject = iSpecificationDescription.BaseObjects.Item(0)
print(BaseObject.AttributeNumber)
Есть подозрение, что это можно сделать так:
iAttribute = iKompasDocument1.Attributes(0, 0, 0, 0, 0, BaseObject)
Но нет, ничего не передается. Наверное вместо нулей что-то другое надо? Или это вообще не то...
Вот интерфейс IProperty и IPropertyKeeper. Просто я как-то без этого обошелся и у меня для молели раздел либо стандартные, либо детали.
Посмотрите функцию, там много чего.
function Create3D(name, design, path, filename: string): boolean;
var
iDoc3D: ksDocument3D;
ColorParam: ksColorParam;
iPart, phantom: ksPart;
EntityCollection, ecoll: ksEntityCollection;
EntityConcentric, EntityConcidence, EntityConcentric2, EntityConcidence2
: ksEntity;
EntityFaceDistance1, EntityFaceDistance2: ksEntity;
iRequestInfo3D: ksRequestInfo3D;
ikompas: KompasObject;
Place: ksPlacement;
iUserParam: ksUserParam;
VarCol: ksVariableCollection;
variable: ksVariable;
strPromt, strPromt1: string;
i, count: integer;
begin
ikompas := KompasObject(GetActiveOleObject('Kompas.Application.5'));
if ikompas = nil then
Exit;
iDoc3D := ksDocument3D(ikompas.ActiveDocument3D);
if iDoc3D = nil then
Exit;
iPart := ksPart(iDoc3D.GetPart(pNew_Part));
// ColorParam := ksColorParam(ipart.ColorParam);
// ColorParam.Color := StUnit.ColorVar; // 15387420-голубой;//16744448-синий;
// iPart.SetAdvancedColor(ColorParam.Color, 0.5, 0.6, 0.5, 0.8, 0.8, 1);
// iPart.Update;
iUserParam := ksUserParam(ikompas.GetParamStruct(ko_UserParam));
iUserParam.number := 1;
iPart.standardComponent := true;
iPart.name := Name;
iPart.filename := path + filename;
iRequestInfo3D := ksRequestInfo3D(iDoc3D.GetRequestInfo(iPart));
iRequestInfo3D.prompt := 'Укажите положение стандартного изделия';
iRequestInfo3D.SetCallBack('SELECTCALLBACKPROC', hinstance, nil);
iRequestInfo3D.SetFilterCallBack('SELECTFILTERPROC', hinstance, nil);
iRequestInfo3D.CreatePhantom;
phantom := ksPart(iRequestInfo3D.GetIPhantom);
phantom.standardComponent := true;
// iDoc3D.drawMode := vm_Shaded;
// phantom1 := ksFeatureCollection(iRequestInfo3D.GetIPhantom);
VarCol := ksVariableCollection(phantom.VariableCollection);
// ShowMessage('here');
count := VarCol.GetCount();
// ShowMessage(intToStr(count));
if StUnit.DBGrid2.Visible = false then
begin
for i := 0 to count - 1 do
begin
variable := ksVariable(VarCol.GetByIndex(i));
if variable.name = 'h' then
variable.value := StUnit.DBGrid1.DataSource.DataSet.FieldByName
('h').AsFloat;
// ShowMessage(StUnit.DBGrid1.DataSource.DataSet.FieldByName('h').AsString);
if variable.name = 'h1' then
variable.value := StUnit.DBGrid1.DataSource.DataSet.FieldByName
('h1').AsFloat;
if variable.name = 'l' then
variable.value := StUnit.DBGrid1.DataSource.DataSet.FieldByName
('l').AsFloat;
if variable.name = 'l1' then
variable.value := StUnit.DBGrid1.DataSource.DataSet.FieldByName
('l1').AsFloat;
if variable.name = 'a' then
variable.value := StUnit.DBGrid1.DataSource.DataSet.FieldByName
('a').AsFloat;
if variable.name = 'd' then
variable.value := StUnit.DBGrid1.DataSource.DataSet.FieldByName
('d').AsFloat;
end;
// kompas.ksExecuteKompasCommand(prMovePart, True);
end
else
begin
for i := 0 to count - 1 do
begin
// ShowMessage('here');
variable := ksVariable(VarCol.GetByIndex(i));
if variable.name = 'd1' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('d1').AsFloat;
if variable.name = 'D' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('D').AsFloat;
if variable.name = 'k' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('k').AsFloat;
if variable.name = 'r1' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('r1').AsFloat;
if variable.name = 'h' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('h').AsFloat;
if variable.name = 'b' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('b').AsFloat;
if variable.name = 'l' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('Lenght').AsFloat;
if variable.name = 'S' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('S').AsFloat;
if variable.name = 'e' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('e').AsFloat;
if variable.name = 'fas' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('fas').AsFloat;
if variable.name = 'h' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('h').AsFloat;
// ShowMessage(DBGrid2.DataSource.DataSet.FieldByName('h').AsString);
if variable.name = 'h1' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('h1').AsFloat;
if variable.name = 'i' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('i').AsFloat;
if variable.name = 'l1' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('l1').AsFloat;
if variable.name = 'a' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('a').AsFloat;
if variable.name = 'd' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('d').AsFloat;
if variable.name = 't' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('t').AsFloat;
if variable.name = 'r' then
variable.value := StUnit.DBGrid2.DataSource.DataSet.FieldByName
('r').AsFloat;
end;
phantom.RebuildModel;
end;
// phantom := kspart(iRequestInfo3D.GetIPhantom);
if StUnit.mate then
begin
EntityCollection := ksEntityCollection(ipart.EntityCollection(o3d_face));
EntityConcentric := ksEntity(phantom.GetDefaultEntity(o3d_axisOZ));
EntityConcidence := ksEntity(phantom.GetDefaultEntity(o3d_planeXOY));
strPromt := 'Укажите отверстие';
strPromt1 := 'Укажите плоскость совпадения';
EntityConcentric2 := ksEntity(iDoc3D.UserSelectEntity(nil,
'SELECTFILTERPROC', strPromt, hinstance, nil));
EntityConcidence2 := ksEntity(iDoc3D.UserSelectEntity(nil,
'SELECTFILTERPROC', strPromt1, hinstance, nil));
if iDoc3D.UserGetPlacementAndEntity(2) then
begin
iPart.SetPlacement(iRequestInfo3D.GetPlacement);
iDoc3D.SetPartFromFile(path + filename, iPart, true);
iDoc3D.AddMateConstraint(mc_Concentric, EntityConcentric,
EntityConcentric2, integer(StUnit.direction), 1, 0);
iDoc3D.AddMateConstraint(0, EntityConcidence, EntityConcidence2,
integer(StUnit.direction), 1, 0);
iPart.UpdatePlacement;
end;
iPart.SetUserParam(iUserParam);
iPart.marking := design;
// iPart.useColor := 3;
end
else
begin
if iDoc3D.UserGetPlacementAndEntity(0) then
begin
iPart.SetPlacement(iRequestInfo3D.GetPlacement);
iDoc3D.SetPartFromFile(path + filename, iPart, true);
iPart.UpdatePlacement;
end;
iPart.SetUserParam(iUserParam);
iPart.marking := design;
// iPart.useColor := 3;
end;
// ColorChange(iPart);
iPart.useColor := 0;
ColorParam := ksColorParam(ipart.ColorParam);
// ipart.marking := 'test';
ColorParam.Color := StUnit.ColorVar; // 15387420-голубой;//16744448-синий;
// iPart.SetAdvancedColor(ColorParam.Color, 0.5, 0.6, 0.5, 0.8, 0.8, 1);
iPart.Update;
end;
Спасибо за ответ. Попробую посмотреть когда время будет. :w:
Подскажите, пожалуйста, а как извлечь Атрибуты Документа?
attr.jpg
Я делаю так:
...
IKompasDocument3DPtr doc3D(newKompasAPI->ActiveDocument);
res = doc3D.QueryInterface(IID_IKompasDocument1, &kd1);
_variant_t at = kd1->GetAttributes(0, 0, 0, 0, 0, NULL); // Здесь получаю атрибуты
PrintAttr(at);
Собственно, функция PrintAttr:
void PrintAttr(const _variant_t& varArr)
{
if (varArr.vt == (VT_ARRAY | VT_DISPATCH))
{
int count = varArr.parray->rgsabound[0].cElements - varArr.parray->rgsabound[0].lLbound; // Здесь все прекрасно, у меня 2 атрибута и count = 2, ура
if (varArr.parray->cDims == 1)
{
HRESULT hr;
LPDISPATCH HUGEP *pvar;
hr = ::SafeArrayAccessData(varArr.parray, (void HUGEP* FAR*)&pvar);
if (!FAILED(hr) && pvar)
{
for (int i = 0; i < count; i++)
{
long idx = i;
// Хочется вызвать SafeArrayGetElement, но что я получу? Какого типа будет этот атрибут?
hr = SafeArrayGetElement(varArr.parray, &idx, ????); // [1]
}
hr = ::SafeArrayUnaccessData(varArr.parray);
}
}
}
}
Хочется вызвать SafeArrayGetElement, но что я получу? Какого типа будет этот атрибут?
Никакого IAttribute я не нашел.
Далее, у меня один атрибут строковый, другой числовой. Это как-то можно различить? Ну, то есть, тип его получить (строковый/числовой).
Был не прав, IAttribute есть. Теперь цикл for выглядит так:
for (int i = 0; i < count; i++)
{
long idx = i;
IAttributePtr attr;
IDispatchPtr dis;
hr = SafeArrayGetElement(varArr.parray, &idx, &attr);
dis = attr;
GetType(dis);
// Ну, и вызываем любой метод объекта IAttribute, например, GetType():
KompasAPIObjectTypeEnum type = attr->GetType();
}
Ну, и вызываем любой метод объекта IAttribute, например, GetType(). Можно любой другой, результат один - Access Violation. Такое впечатление, что attr указатель на какой-то иной интерфейс, а не IAttribute. Что же это тогда? Как узнать?
В попытке самостоятельно ответить на этот вопрос, написал функцию GetType():
void GetType(IDispatchPtr attr)
{
ITypeInfoPtr pInfo;
HRESULT hr = attr->GetTypeInfo(0, 0, &pInfo);
TYPEATTR * ta = (TYPEATTR *)malloc(sizeof(TYPEATTR));
pInfo->GetTypeAttr(&ta); // [1]
}
На последней строчке (то есть [1]) ставлю бряку. Структура ta имеет поле guid, так вот оно равно IID_IAttribute.
То есть attr все-таки указывает на интерфейс IAttribute? Тогда почему происходит крэш (Access Violation)?
Так это выглядит
Screenshot_3.png
В документе создал 2 атрибута
теперь их читаю
# -*- coding: utf-8 -*-
import pythoncom
from win32com.client import Dispatch, gencache
# Подключим описание интерфейсов API7
kompas_api7_module = gencache.EnsureModule("{69AC2981-37C0-4379-84FD-5DD2F3C0A520}", 0, 1, 0)
application = kompas_api7_module.IApplication(
Dispatch("Kompas.Application.7")._oleobj_.QueryInterface(kompas_api7_module.IApplication.CLSID,
pythoncom.IID_IDispatch))
kompas_document = application.ActiveDocument
iAttrTypeMng = kompas_api7_module.IAttrTypeMng(application)
print(iAttrTypeMng)
attributes = iAttrTypeMng.GetAttrTypes(kompas_document)
print(attributes)
"""
ksATUnknown -1 Неизвестный
ksATString 0 Строка
ksATDouble 1 Число
ksATFixedTable 2 Таблица с фиксированным числом строк
ksATVariableTable 3 Таблица с переменным числом строк
"""
for attr in attributes:
type_name = attr.TypeName
print(type_name)
attr_type = attr.AttrType
print(attr_type)
Screenshot_4.png
вот, что получаю на выходе
0 и 1 это тип данных
0 - Строка
1 - Число
Можно так
# -*- coding: utf-8 -*-
import pythoncom
from win32com.client import Dispatch, gencache
# Подключим описание интерфейсов API7
kompas_api7_module = gencache.EnsureModule("{69AC2981-37C0-4379-84FD-5DD2F3C0A520}", 0, 1, 0)
application = kompas_api7_module.IApplication(
Dispatch("Kompas.Application.7")._oleobj_.QueryInterface(kompas_api7_module.IApplication.CLSID,
pythoncom.IID_IDispatch))
kompas_document = application.ActiveDocument
kompas_document_1 = kompas_api7_module.IKompasDocument1(kompas_document)
attributes = kompas_document_1.Attributes(0, 0, 0, 0, 0, kompas_document)
print(attributes)
for attr in attributes:
value = attr.Value(0, 0)
print(value)
attribute_type = attr.AttributeType
# print(attribute_type)
type_name = attribute_type.TypeName
print(type_name)
attr_type = attribute_type.AttrType
print(attr_type)
Screenshot_5.png
ПРивет и 140.0 это значения
Screenshot_6.png
Получилось. На С++, правда, все выглядит не так просто как на Питоне. Но как-то работает.
Теперь добавил еще два атрибута, один - таблица с фиксированной длиной, второй - с произвольной. И опять "забуксовал"
// Атрибут - таблица:
IAttributePtr attr(pvar[i]);
long cc = attr->GetColumnsCount(); // Тут верное кол-во строк
long rc = attr->GetRowsCount(); // Верное кол-во столбцов
VARIANT colKey = attr->GetColumnKey();
for (long i = 0; i < cc; i++)
{
VARTYPE vt = colKey.vt; // vt = 0x02003, то есть VT_ARRAY|VT_I4 или, по-русски говоря, массив int-ов
if (colKey.parray)
{
int count = val.parray->rgsabound[0].cElements - val.parray->rgsabound[0].lLbound; // [1] -Access Violation
ULONG elems = val.parray->cbElements; // [2] -Access Violation
}
}
То есть, беру атрибут таблицу. Могу получить кол-во строк и столбцов, но не знаю, как их использовать.
Могу через вызов GetColumnKey() получить массив int-ов, но не могу работать с ним как с массивом, в строках [1] и [2] будет Access Violation
я не совсем понял зачем вам ключи. Если надо получить все значения из таблицы то values = attr.Values
возвращается кортеж всех значений из таблицы.
для примера
(' АБВГД.001', ' АБВГД.002', ' АБВГД.003', ' Пластина', ' Кронштейн', ' Проушина', 10, 5, 1, ' Детали', ' Детали', ' Детали')
Screenshot_7.png
Если надо получить какое то определенное значение тогда так
value =attr.Value( RowNumb, ColumnNumb )
RowNumb (long) - номер строки атрибута
ColumnNumb (long) - номер столбца атрибута.
Для эксперимента присвоил каждому столбцу свой ключ:
Столбцу обозначение - 1
наменованию - 2
количеству - 3
разделу - 4
после выполнения
column_key = attr.ColumnKey
получил кортеж с ключами
(1, 2, 3, 4)
Цитата: Denis78 от 21.04.22, 13:52:22Был не прав, IAttribute есть. Теперь цикл for выглядит так:
for (int i = 0; i < count; i++)
{
long idx = i;
IAttributePtr attr;
IDispatchPtr dis;
hr = SafeArrayGetElement(varArr.parray, &idx, &attr);
dis = attr;
GetType(dis);
...
}
Ну, и вызываем любой метод объекта IAttribute, например, GetType(). Можно любой другой, результат один - Access Violation. Такое впечатление, что attr указатель на какой-то иной интерфейс, а не IAttribute. Что же это тогда? Как узнать?
Правильно, и в хелпе написано, что тип данных - IDispatch. И ты сам проверяешь тип варианта на VT_DISPATCH в предыдущих сообщениях. Однако почему-то считаешь, что у тебя интерфейс IAttribute. С чего? Только с того, что ты так написал? А функция SafeArrayGetElement не делает преобразование интерфейсов, она просто копирует содержимое!
Вот у тебя правильно было написано (без всяких этих заморочек с HUGEP и FAR):
LPDISPATCH *pvar;
hr = ::SafeArrayAccessData(varArr.parray, (void**)&pvar);
Следовательно, pvar[ i ] и есть требуемый интерфейсный указатель, т.е. уже не нужно обращаться к SafeArrayGetElement, хотя можно и через эту функцию, но тогда нужно поменять местами принимаемые интерфейсы:
1)
IAttributePtr attr = pvar[i];
2)
IDispatchPtr dis;
hr = SafeArrayGetElement(varArr.parray, &idx, &dis);
IAttributePtr attr = dis; // Вот здесь будет запрошен интерфейс IAttribute и можно безопасно вызвать его методы
Цитата: Denis78 от 22.04.22, 14:22:10int count = val.parray->rgsabound[0].cElements - val.parray->rgsabound[0].lLbound; // [1] -Access Violation
ULONG elems = val.parray->cbElements;
Ну а что это за переменная val?
Причём поле cElements и есть число значений, не нужно из него вычитать что-либо. Вот у массива есть функции SafeArrayGetUBound и SafeArrayGetLBound, вот там нужно оперировать их разностью.
VARIANT colKey = attr->GetColumnKey();
...
if (colKey.vt == (VT_ARRAY|VT_I4))
{
int* colItems;
hr = ::SafeArrayAccessData(colKey.parray, (void**)&colItems);
for(i=0; i<count; i++)
; // используем значение атрибута colItems[i] как целое число
hr = ::SafeArrayUnaccessData(colKey.parray);
}