Полная версия этой страницы:
VBA for ACAD
Может есть тут специалисты по VBА, хочу для ACADа на VBA реализовать след. вещь - поиск элементов (линий, блоков и т.д.) аналог команды - "_qselect". Для начала надо найти в чертеже все блоки с именем "*Ux", где х - лубое число от 0 до 1000 и выбрать их... можте кто поможет, подскажет...
или может кто пример скинет, чтобы просто выбирал все линии, а я потом разберусь как нить... Спасибо... Жду...
blackpoint
4.05.2009 - 22:35
Public Sub purgeLines()
Dim obj As Object
For Each obj In ModelSpace
If TypeName(obj) = "IAcadLine" Then
Dim line As AcadLine
Set line = obj
line.Delete
End If
Next
End Sub
blackpoint
4.05.2009 - 23:04
Для ссылок на блоки (сами определения остаются):
Код
Public Sub purge()
Dim obj As Object
For Each obj In ModelSpace
If TypeName(obj) = "IAcadBlockReference" Then
Dim blckref As AcadBlockReference
Set blckref = obj
If blckref.Name = "test" Then
blckref.Delete
End If
End If
Next
End Sub
А что делать, если блоки лежат в пространствах листов?
По-моему, более верным будет код наподобие
Код
Option Explicit
Public Sub EraseBlocks(Optional sBlockName As String)
Dim blkRef As AcadBlock
If IsMissing(sBlockName) Then sBlockName = "*"
On Error Resume Next
For Each blkRef In ThisDrawing.Blocks
blkRef.Erase
Next blkRef
Dim oLayout As AcadLayout, blkLay As AcadBlock, oEnt As AcadEntity
For Each oLayout In ThisDrawing.Layouts
Set blkLay = oLayout.Block
For Each oEnt In blkLay
If oEnt.ObjectName = "AcDbBlockReference" And UCase(oEnt.Name) Like UCase(sBlockName) Then
oEnt.Delete
End If
Next oEnt
Next oLayout
End Sub
Вечно путаю методы .Erase и .Delete, так что проверяйте.
я как то сделал быстрее...
Цитата
Dim blockObj As AcadBlock
MsgBox "я нашел - " & ThisDrawing.Blocks.count - 3 & " блока(ов)"
For k = 3 To ThisDrawing.Blocks.count - 1
Next k
но я нашел блоки не на листе, а существующие блоки, среди имен "count" есть и мои блоки,
имя я могу найти как "ThisDrawing.Blocks.Item(k).Name", где к от 3 до ThisDrawing.Blocks.count - 1,
но дальше тупик, как применить Explode, к AcadBlock нельзя, только к AcadBlockReference.
Как мне перейти от AcadBlock к AcadBlockReference? или у меня путь не тот...
т.к. как найти какие блоки вставлены в чертеж и к ним применить Explode?
---
пример посмотрю, спасибо...
Ты вот что определи - у тебя имена блоков твоих есть? Если да, то как вариант (я код не проверял):
Код
Type typLayer
oPointer As Object
IsLock As Boolean
IsFrozen As Boolean
End Type
Public Sub ExplodeBlocksExcule(Optional sBlockNameExclude As String)
If IsMissing(sBlockNameExclude) Then
sBlockNameExclude = "*"
Else
sBlockNameExclude = UCase(sBlockNameExclude)
End If
Dim oLayer As AcadLayer, arLayers() As typLayer, iCounter As Integer
ReDim arLayers(ThisDrawing.Layers.Count - 1)
iCounter = 0
On Error Resume Next
For Each oLayer In ThisDrawing.Layers
arLayers(iCounter).oPointer = oLayer
arLayers(iCounter).IsFrozen = oLayer.Freeze
arLayers(iCounter).IsLock = oLayer.Lock
oLayer.Freeze = False
oLayer.Lock = False
Next oLayer
On Error GoTo 0
Dim oBlockDef As AcadBlock, oEnt As AcadEntity
For Each oBlockDef In ThisDrawing.Blocks
For Each oEnt In oBlockDef
If oEnt.ObjectName = "AcDbBlockReference" And UCase(oEnt.Name) Like sBlockNameExclude Then
oEnt.Explode
oEnt.Erase
End If
Next oEnt
Next oBlockDef
On Error Resume Next
For iCounter = 0 To UBound(arLayers)
arLayers(iCounter).oPointer.Lock = arLayers(iCounter).IsLock
arLayers(iCounter).oPointer.Freeze = arLayers(iCounter).IsFrozen
Next iCounter
End Sub
уфф... я не такой спец по VBA, в этом коде ничего не понимаю... я думал - все просто...
есть ACADBlocks в чертеже (допусти три блока с именем 1, 2 и 3)
блоки 1 и 2 уже вставлены в чертеж, а блок 3 просто существует...
мне надо найти из блок с именем 2 и разчленить его... буду сам думать...
Я тоже предпочитаю VisualLISP
А так-то по логике все просто: сначала разблокируются и включаются все слои; потом проходим по коллекции блоков. Внутри каждого описания блока проходим по элементам этого блока (пользуясь тем, что, например, пространство модели - тоже описание блока). Если, уже находясь внутри описания какого-то блока, натыкаемся на AcDbBlockReference (то есть вхождение блока), и имя этого блока похоже на sBlockNameExclude, то мы этот элемент расчленяем и удаляем. А потом восстановление состояния слоев в исходное состояние.
P.S. Вызывать код можно как
Код
Sub Test()
ExplodeBlocksExcule 'разбить вообще все блоки
End Sub
Sub Test1()
ExplodeBlocksExcule "2" 'Разбить все блоки с именем "2"
End Sub
Sub Test2()
ExplodeBlocksExcule "*2*" ' Разбить все блоки с именами, в которых содержится цифра 2
End Sub
короче сделал я все проще
Цитата
Private Sub CommandButton1_Click()
Dim blockObj As AcadBlock
If Left(ThisDrawing.Blocks.Item(k).Name, 2) = "имя" Then
stopflag = Val(Right(ThisDrawing.Blocks.Item(k).Name, Len(ThisDrawing.Blocks.Item(k).Name) - 2))
End If
Dim objSelSet As AcadSelectionSet
Dim objSelCol As AcadSelectionSets
Dim intType(0) As Integer
Dim varData(0) As Variant
Set objSelCol = ThisDrawing.SelectionSets
For Each objSelSet In objSelCol
If objSelSet.Name = "blocks" Then
objSelSet.Delete
Exit For
End If
Next
Set objSelSet = ThisDrawing.SelectionSets.Add("blocks")
mode = acSelectionSetAll
intType(0) = 0 'Фильтры выбора
varData(0) = "INSERT" '"INSERT" для выбора блока
'objSelSet.SelectOnScreen intType, varData
objSelSet.Select mode, 0, 0, intType, varData
Set SelectOnlyOnScreen = objSelSet
Set acSelSet = SelectOnlyOnScreen
For I = 0 To acSelSet.count - 1
Next I
MsgBox "Найдено и разбито " & CStr(acSelSet.count) & " блока(ов)..."
End Sub
но тут со своими тонкостями...
Ага, с тонкостями... Не обрабатываются "вложенные" блоки. В фильтре выбора нет дополнительной фильтрации по имени блока. В коде нет прямого указания на разбитие блока.
я не показывал эту часть...
Цитата
...
Set objSelSet = ThisDrawing.SelectionSets.Add("blocks")
mode = acSelectionSetAll
intType(0) = 0
varData(0) = "INSERT"
objSelSet.Select mode, 0, 0, intType, varData
Set SelectOnlyOnScreen = objSelSet
Set acSelSet = SelectOnlyOnScreen
For i = 0 To acSelSet.count - 1
del_flag = 0
For j = stopflag - acSelSet.count - 1 To stopflag
numb = Right(Str(j), Len(Str(j)) - 1)
block_name = "*U" + numb
If objSelSet.Item(i).Name = block_name Then
objSelSet.Item(i).Explode
del_flag = 1
End If
Next j
If del_flag = 1 Then objSelSet.Item(i).Delete
Next i
Вчера только начал разбираться с МИФом для Акада... Книги нет толковой, да и времени...
.Delete просто удалит блок. А где разбитие?
Я зануда та еще
Цитата(kpblc @ 5.05.2009 - 14:03)
.Delete просто удалит блок. А где разбитие?
Я зануда та еще
поправил...
книгу бы лучше бы дал какую на русском
хорошую
А нету у меня такой книги. Встроенной справки зачастую достаточно. Правда, на английском
А для сложных вопросов и существуют форумы
kpblc, вот у меня objSelSet.Item(i).Name - это имя блока, objSelSet.Item(stopflag - 1).Layer - имя слоя этого блока...
как мне тут сделать фильтр, что если слой с именем objSelSet.Item(stopflag - 1).Layer заблокирован, то то-то
objSelSet.Item(stopflag - 1).Layer.Lock = False не получается
т.е. хочу написать
if objSelSet.Item(stopflag - 1).Layer(слой) = заблокирован, то objSelSet.Item(i).Delete не надо!!!
Помоги, а!!!
Сейчас лиспом машина занята по самое не хочу, поэтому пишу насухую
Код
If Not (ThisDrawing.Layers.Item(objSelSet.Item(i).Layer).Lock) Then
objSelSet.Item(i).Explode
objSelSet.Item(i).Erase
End If
по идее для коллекции слоев допускается и нормально срабатывает Item(ИмяСлоя).
---
Исправил 1 опечатку и положение 1 скобки
Спасибо... работает... эх... я тоже так хочу "шарить" в VBA...
добавил строчку с Freeze у себя
Пока не забыл: для полноты картины я бы задумался еще и о замороженных слоях.
<offtop>Я немного соображаю в лиспе и на основе этого пишу VBA-шный код... Грубо говоря, "портирую" код
Но за добрые слова спасибо
Приятно, черт возьми
</offtop>
слушай, а ты не знаеешь, как сделать так, чтобы пока команда ThisDrawing.SendCommand, посланная в командрую строку не выбрала рамку (я не выбрал рамку
), остальной макрос не выполнялся?
Стоп. А что ты посылаешь в ком.строку? Если набор формировать, то смотри SelectSets.Add (да и так у тебя уже есть аналог; ключи только надо поменять).
Цитата(kpblc @ 5.05.2009 - 15:14)
Стоп. А что ты посылаешь в ком.строку? Если набор формировать, то смотри SelectSets.Add (да и так у тебя уже есть аналог; ключи только надо поменять).
ай по одну прогу тут пишу, под Магик... там свои наборы, команды и заморочки...
Под подобные приложения вообще тяжело что бы то ни было пишется. Мне в свое время пришлось "забирать" данные с объектов СПДС 4.0, вот я тогда порадовался, что сделал ставку на лисп... Другими методами добраться до значений тех же узловых выносок было никак невозможно. Ну или использовать C++ / C# / VB.NET и работать уже глубоко внутри ядра. А это надо время учиться, которого мне никто не выделял
Ладно, это лирика. А по поводу SendCommand... Я бы, наверное, сделал так:
1. На лиспе пишется нечто типа
Код
(defun execute-command (cmdname)
(command cmdname)
(while (/= (getvar "cmdactive") 0)
(command pause)
);_ end of while
);_ end of defun
2. Полученный файл загружается в текущий чертеж (любым из доступных методов)
3. В VBA выполняется нечто типа
Код
ThisDrawing.SendCommand "(execute-command " & Chr(34) & ИмяВыполняемойКоманды & Chr(34) & ")" & vbCr
Только учти, что я подобными играми занимался примерно года три назад, с тех пор так ни разу и не пригодилось. Так что работоспособность метода под бааальшим вопросом.
я отправляю
Цитата
ThisDrawing.SendCommand "magiexp" & vbCr & "все" & vbCr & vbCr
перед этим делаю Hide, а потом уже обработка набора... но если не ввести столько вводов, то сначало обработается набор, а потом уже команда выполнится, а я хочу, чтобы пока в командной строке не появилось "Команда", макрос бы не выполнялся...
Пойми, я лисповик
Посему и решение предлагаю "хоть чутка поближе к себе"
Код
(defun execute-magiexp ()
(command magiexp "" "_all" "" "")
(while (/= (getvar "cmdactive") 0)
(command pause)
);_ end of while
);_ end of defun
А в остальном как и было раньше сказано..
Крыс, а не знаешь как определить количество листов в четреже, в листах количество ВЭ, а потом работать с ВЭ?
Количество листов:
Код
ThisDrawing.Layouts - 1
Если надо получать все ВЭ, то либо идти через SelectionSet, либо пробовать нечто типа
Код
Function GetAllViewPorts() As Variant
Dim Res() As Object
Dim oLayout As AcadLayout, oEnt As AcadEntity
For Each oLayout In ThisDrawing.Layouts
If Not (UCase(oLayout.Name) Like "*[*]") Then
On Error GoTo lErrorReDim
For Each oEnt In oLayout.Block
If oEnt.ObjectName = "AcDbViewport" Then
ReDim Preserve Res(UBound(Res) + 1)
Set Res(UBound(Res)) = oEnt
End If
Next oEnt
End If
Next oLayout
GetAllViewPorts = Res
Exit Function
lErrorReDim:
ReDim Res(0)
Resume Next
End Function
Sub Test()
Dim arVP As Variant
arVP = GetAllViewPorts
End Sub
Код я на корректность результата не проверял - тупо некогда
If Not (UCase(oLayout.Name) Like "*[*]") Then что эта чтрочка означает?
-может на по асе лучше, если ты согласен
а то двоем, не удобно как то
Эта строчка означает, что в имени Layout'а не должно быть последней звездочки. Например, пространство модели (кстати, тоже Layout, тоже имеет внутри себя Viewport) имеет имя *Model_Space*; коллекция пространств листа - *Paper_Space*. Для полной гарантии просто тупо исключил из обработки
Насчет аськи... У меня сейчас предотпускная свистопляска, нормально пообщаться точно не удастся. А так-то, стучись, конечно.
строчка For Each oLayout In ThisDrawing.Layouts мне понятка,
а вот какая выбирает ВЭ? если "oEnt As AcadEntity", где AcadEntity - все ВЭ... то я такого в хелпе не наше...
может AcadViewport? или я запутался?
Попробуем так:
Код
For Each oLayout In ThisDrawing.Layouts 'По каждому объекту пространства в коллекции пространств
If Not (UCase(oLayout.Name) Like "*[*]") Then ' Если имя не содержит символа "*"
On Error GoTo lErrorReDim ' Ну, это обработчик ошибок, ничего нестандартного
For Each oEnt In oLayout.Block ' По каждому объекту внутри пространства. Пофигу какому - объект есть, его обрабатываем
If oEnt.ObjectName = "AcDbViewport" Then 'Если имя объекта AcDbViewport, т.е. объект является ВЭ
ReDim Preserve Res(UBound(Res) + 1) 'В массив результатов добавить 1 элемент
Set Res(UBound(Res)) = oEnt 'И только что добавленному элементу массива присвоить указатель на обрабатываемый примитив
End If
Next oEnt
End If
Next oLayout
тогда как его сделать активным...
а если два ВЭ, то имена у них одинаковые?
Стоп. У видового экрана, по-моему, нет понятия имени. Указатель - есть. Координаты - есть. Имя объекта - есть (если честно, то оно у любого примитива есть).
А как ты вообще собираешься "понимать", какой ВЭ надо активизировать?
Цитата(kpblc @ 7.05.2009 - 11:47)
А как ты вообще собираешься "понимать", какой ВЭ надо активизировать?
можно разбить "Model" на несколько "подэкранов", но это не то, мне надо поработать на Листах, в коротых будут ВЭ и не один...
AcadViewport - как раз обрабатывает ВЭ на моделе, мне так показалось...
---
вот пример нашел... ща забацаю...
Цитата
Sub Example_ActivePViewport()
Dim newPViewport As AcadPViewport
Dim centerPoint(0 To 2) As Double
Dim height As Double
Dim width As Double
height = 5#
width = 5#
centerPoint(0) = 5#: centerPoint(1) = 5#: centerPoint(2) = 0#
' Создайте в пространстве листа объект Viewport
ThisDrawing.ActiveSpace = acPaperSpace
Set newPViewport = ThisDrawing.PaperSpace.AddPViewport(centerPoint, width, height)
ZoomAll
newPViewport.DISPLAY (True)
' Перед созданием в пространстве листа активного Viewport,
' свойство mspace должно быть True
ThisDrawing.mspace = True
ThisDrawing.ActivePViewport = newPViewport
End Sub
Так это создание ВЭ. А не активация уже существующего. Немного другая задача, мне кажется.
Все написал... Модуль умеет скать все листы, находить на них ВЭ и активировать их по очереди... активируя производить операцию внутри ВЭ...
блин, на PaperSpace два ВЭ, а AcDbViewport_ов 5... ? что еще может прнниматься за AcDbViewport, внешняя ссылка, вставленная поверх ВЭ может? как ее отсеять от ВЭ, т.к. ее нельзя сделать Active?
А что получается, если идти через SelectionSet?
Кстати, какова общая задача? Может, и без активации ВЭ можно обойтись?
все получилось... просто надо было добавить
Цитата
On Error Resume Next
вот текст, который у меня получился
(модуль перебирает все листы в чертеже, в каждом листе делает активными все ВЭ по очереди...
Цитата
Private Sub {name}_Click()
{forms}.Hide
Dim Layout As AcadLayout
Dim I, II As Integer
For I = 1 To ThisDrawing.Layouts.Count - 1
For II = 0 To ThisDrawing.Layouts.Item(I).Block.Count - 1
If ThisDrawing.Layouts.Item(I).Block.Item(II).ObjectName = "AcDbViewport" Then
ThisDrawing.ActiveLayout = ThisDrawing.Layouts.Item(I)
ThisDrawing.Layouts.Item(I).Block.Item(II).Display (True)
ThisDrawing.MSpace = True
On Error Resume Next
ThisDrawing.ActivePViewport = ThisDrawing.Layouts.Item(I).Block.Item(II)
ThisDrawing.SendCommand {команда автокада или несколько}
End If
Next
ThisDrawing.MSpace = False
Next
End Sub
Я почти уверен, что можно обойтись без SendCommand и просто менять либо ViewPort, либо пространство модели, на которое он (ВЭ) указывает...
Цитата(kpblc @ 12.05.2009 - 13:31)
Я почти уверен, что можно обойтись без SendCommand и просто менять либо ViewPort, либо пространство модели, на которое он (ВЭ) указывает...
сенд команд это просто можно убрать - это уже специфическая команда выполняется...
---
Крыс, ты не знаешь как можно просто комбобокс отсортировать? может команда есть какая?
oleg2007
5.09.2009 - 00:21
Подскажите какой командой выбрать все объекты например линии из всех выбранных объектов?
Dim objCount As Integer
Dim I As Integer
objCount = ThisDrawing.ModelSpace.Count
For I = 3 To ThisDrawing.Blocks.Count - 1
MsgBox ThisDrawing.Blocks.Item(I).Name
Next
Думаю, сам потом разберешься...
Вопрос: объекты, входящие в блоки - учитывать или нет? Если не учитывать входящие в блоки, то кто мешает создать соответствующий SelectionSet с фильтром по имени объекта?
Вот я знаю как найти например однострочный текст (как Крыс описал), как изменить свойства текта каждого отдельно и по порядку, а как ВЫБРАТЬ весь текст, Крыс, не подскажешь (чтобы можно было изменять свойства всего и сразу)?
Если текст, входящий в блоки, исключается из обработки, то опять же - SelectionSet (ох, сколько ж я с VBA не работал... Попробую наваять, но за корректность работы не отвечаю):
Код
Option Explicit
Type LayerEnt
name As String
IsLocked As Boolean
IsFrozen As Boolean
End Type
Private Function LayerStatusSave() As LayerEnt()
Dim res() As LayerEnt, ArSize As Integer
Dim LayerItem As AcadLayer
For Each LayerItem In ThisDrawing.Layers
If Not (LayerItem.name Like "*|*") Then
On Error GoTo lErrorReDim
ReDim Preserve res(UBound(res))
ArSize = UBound(res)
res(ArSize).name = LayerItem.name
res(ArSize).IsLocked = LayerItem.Lock
res(ArSize).IsFrozen = LayerItem.Freeze
On Error Resume Next
LayerItem.Lock = False
LayerItem.Freeze = False
End If
Next
LayerStatusSave = res
Exit Function
lErrorReDim:
ReDim res(0)
Resume Next
End Function
Private Function LayerStatusRestore(ByRef LayerStatusList() As LayerEnt)
Dim Item As Integer
With ThisDrawing.Layers
For Item = LBound(LayerStatusList) To UBound(LayerStatusList)
On Error Resume Next
.Item(LayerStatusList(Item).name).Lock = LayerStatusList(Item).IsLocked
.Item(LayerStatusList(Item).name).Freeze = LayerStatusList(Item).IsFrozen
Next
End With
ReDim LayerStatusList(0)
On Error GoTo 0
End Function
Public Sub test()
Dim lst() As LayerEnt, SelSetName As String, SelSet As AcadSelectionSet
Dim dxtInt() As Integer, dxfType() As Variant
SelSetName = "lavteam_selectionset"
On Error Resume Next
With ThisDrawing.SelectionSets
.Item(SelSetName).Clear
.Item(SelSetName).Delete
Set SelSet = .Add(SelSetName)
ReDim dxfint(0): ReDim dxfType(0)
dxfint(0) = 0: dxfType(0) = "TEXT"
SelSet.Select acSelectionSetAll, dxfint, dxfType
End With
lst = LayerStatusSave()
Dim objpointer As AcadEntity
For Each objpointer In SelSet
' Делаем чего-то
objpointer.color = acRed
Next
LayerStatusRestore lst
ThisDrawing.Regen acAllViewports
End Sub
P.S. Если надо обрабатывать внутри блоков, то ситуация изменится: надо будет делать, скорее всего, рекурсию для доступа ко всем объектам внутри вставленных блоков. На лиспе я такое сделаю достаточно быстро и просто, на VBA - надо проверять.
А можно "на пальцах"...
Для развития мысли нужно следующее:
1. вот есть два слова.
2. надо их выбрать
3. поменять их накрон
как это сделать поочередно, я знаю, а как сделать выбрать весь тект и поменять наклон в выбранном тексте?
Не надо целого макроса... нужна логика
SelSet.Select acSelectionSetAll, dxfint, dxfType это и есть добавление в набор, где SelSet - сам нам нужный набор?
Так а логика-то проста: создается SelectionSet с фильтром; размораживаются слои; через For Each проход по каждому элементу SelectionSet'a с модификацией и возврат слоев с исходное состояние...
спасибо... получилось вот в таком варианте:
Цитата
Dim SelSetName As String, SelSet As AcadSelectionSet
Dim dxtInt() As Integer, dxfType() As Variant
SelSetName = "lavteam_selectionset"
On Error Resume Next
With ThisDrawing.SelectionSets
.Item(SelSetName).Clear
.Item(SelSetName).Delete
Set SelSet = .Add(SelSetName)
ReDim dxfint(0): ReDim dxfType(0)
dxfint(0) = 0: dxfType(0) = "TEXT"
SelSet.Select acSelectionSetAll, dxfint, dxfType
End With
Dim objpointer As AcadEntity
For Each objpointer In SelSet
' Делаем чего-то
objpointer.color = acRed
Next
ThisDrawing.Regen acAllViewports
т.е. без работы с разморозкой и заморозкой слоев...
Для ради интересу: сделай объект текста, заблокируй слой, на котором он находится, и потом выполни свой макрос.
Скорее всего выдаст ошику...
Твой макрос и линии делает красными
---
Почему этот примет тоже текст делает красным...?
----------------------------------------------------------
Dim ssetObj As AcadSelectionSet
With ThisDrawing.SelectionSets
.Item(SelSetName).Clear
.Item(SelSetName).Delete
End With
Set ssetObj = ThisDrawing.SelectionSets.Add("SSET")
Dim mode As Integer
mode = acSelectionSetAll 'acSelectionSetCrossing
gpCode = 0
dataValue = "Circle"
ssetObj.Select mode, groupCode, dataCode
For Each objpointer In ssetObj
objpointer.color = acRed
Next
ThisDrawing.Regen acAllViewports
Для просмотра полной версии этой страницы, пожалуйста,
пройдите по ссылке.