1- # -----------------------
2- # Clean DWG DEV BUILD
3- # -----------------------
1+ # ----------------------
2+ # Clean DWG - v1.1.0
3+ # ----------------------
44
55# Destroys instances of the dialog before recreating it
66# This has to go first, before modules are reloaded or the ui var is re-declared.
2626
2727# Misc
2828import os
29+ import traceback
2930
3031
3132# --------------------
@@ -77,13 +78,14 @@ def __init__(self, ui_file, pymxs, parent=MaxPlus.GetQMaxMainWindow()):
7778
7879 # Titling
7980
80- self ._window_title = 'Clean DWG - DEV BUILD '
81+ self ._window_title = 'Clean DWG v1.1.0 '
8182 self .setWindowTitle (self ._window_title )
8283
8384 # ---------------------------------------------------
8485 # Widget Setup
8586 # ---------------------------------------------------
8687
88+ self ._chk_layer = self .findChild (QtW .QCheckBox , 'chk_layer' )
8789 self ._chk_expand = self .findChild (QtW .QCheckBox , 'chk_expand' )
8890 self ._chk_full_scene = self .findChild (QtW .QCheckBox , 'chk_fullScene' )
8991
@@ -110,6 +112,7 @@ def __init__(self, ui_file, pymxs, parent=MaxPlus.GetQMaxMainWindow()):
110112 '[2/4] Building list of Parents / Children...' ,
111113 '[3/4] Making Transform Controllers unique...' ,
112114 '[4/4] Cleaning up Parents...' ,
115+ 'Waiting for 3ds Max to un-freeze...' ,
113116 'Done.' ,
114117 '%s Cleanup failed! Check the Max Listener for details.' % self ._err ]
115118 # Set initial status label
@@ -121,44 +124,93 @@ def __init__(self, ui_file, pymxs, parent=MaxPlus.GetQMaxMainWindow()):
121124 # ---------------------------------------------------
122125 # Private Methods
123126 # ---------------------------------------------------
127+ def _get_children (self , obj , input_list ):
128+ children = obj .children
129+ for child in children :
130+ # Note that since we're calling this from a list of unique hierarchy roots, we don't have to worry about
131+ # checking for duplicate objects. Crawling down the trees will only yield each object one time.
132+ input_list .append (child )
133+ input_list = self ._get_children (child , input_list )
134+ return input_list
124135
125136 # ---------------------------------------------------
126137 # Public Methods
127138 # ---------------------------------------------------
128-
129- # TODO: Set up options checking
130- # TODO: Add functionality for "Expand Selection" and "Clean Entire Scene" options
131-
132139 def clean (self ):
140+ # UI Options
141+ layer = self ._chk_layer .isChecked ()
142+ expand = self ._chk_expand .isChecked ()
143+ scene = self ._chk_full_scene .isChecked ()
144+
145+ # Misc Variables
133146 rt = self ._pymxs .runtime
134- selection = []
135- parents = []
136- children = []
137-
138- try :
139- with self ._pymxs .undo (True , 'Clean DWG' ), self ._pymxs .redraw (False ):
140- # It's much faster to sort the parents and children into layers, rather than deleting objects
141- # Check if these layers already exist, if they do use those. Else, make them.
142- layer_parents = rt .LayerManager .getLayerFromName ('__CLEAN DWG - PARENTS - DELETE' )
143- if layer_parents == None :
144- layer_parents = rt .LayerManager .newLayerFromName ('__CLEAN DWG - PARENTS - DELETE' )
145- layer_children = rt .LayerManager .getLayerFromName ('__CLEAN DWG - CHILDREN - CLEANED UP' )
146- if layer_children == None :
147- layer_children = rt .LayerManager .newLayerFromName ('__CLEAN DWG - CHILDREN - CLEANED UP' )
148-
149- layer_parents .isHidden = True
150147
148+ # Run 3ds Max garbage cleanup before we start. This will wipe the Undo/Redo cache, too.
149+ # Clearing the Undo cache prevents Max from hanging up when the script is used multiple times.
150+ rt .gc ()
151+
152+ with self ._pymxs .undo (True , 'Clean DWG' ), self ._pymxs .redraw (False ):
153+ try :
151154 # 1/4
152155 # Build list of selected objects, optionally including their entire hierarchy.
153156 self ._lbl_status .setText (self ._status [1 ])
154157 selection = rt .getCurrentSelection ()
158+ rt .clearSelection ()
159+
160+ # Clean up selected objects ONLY
161+ if not expand and not scene :
162+ pass
163+
164+ # Expand selection to the full hierarchy of the selected objects
165+ elif expand and not scene :
166+ # Expand up to roots of selected objects
167+ selection_roots = []
168+ self ._bar_progress .setMaximum (len (selection )* 2 )
169+ self ._bar_progress .setValue (0 )
170+ progress = 0
171+ for x in selection :
172+ progress += 1
173+ x_root = x
174+ x_parent = x .parent
175+ while x_parent is not None :
176+ x_root = x_parent
177+ x_parent = x_root .parent
178+ if x_root not in selection_roots :
179+ selection_roots .append (x_root )
180+
181+ self ._bar_progress .setValue (progress )
182+
183+ # Expand down to children of roots
184+ selection_ex = list (selection_roots ) # the list(...) format prevents us passing by reference
185+ progress = len (selection_roots )
186+ self ._bar_progress .setMaximum (progress * 2 )
187+ self ._bar_progress .setValue (progress )
188+ for x in selection_roots :
189+ progress += 1
190+ selection_ex = self ._get_children (x , selection_ex )
191+ self ._bar_progress .setValue (progress )
192+
193+ selection = list (selection_ex )
194+
195+ # Expand selection to entire scene
196+ elif scene :
197+ selection = rt .objects
198+
199+ # Something fishy's going on...
200+ else :
201+ self ._lbl_status .setText (self ._status [6 ])
202+ print "Unknown error, please re-run the Clean DWG script."
203+ return
155204
156205 # 2/4
157206 # Build Parent / Child lists
158207 self ._lbl_status .setText (self ._status [2 ])
159208 self ._bar_progress .setMaximum (len (selection ))
209+ self ._bar_progress .setValue (0 )
160210 progress = 0
161- self ._bar_progress .setValue (progress )
211+
212+ parents = []
213+ children = []
162214 for obj in selection :
163215 if rt .classOf (obj ) == rt .LinkComposite :
164216 parents .append (obj )
@@ -182,33 +234,58 @@ def clean(self):
182234 self ._bar_progress .setValue (progress )
183235
184236 # 4/4
185- # Unparent children and organize objects into layers
237+ # Unparent children, then delete old parents. Optionally move children to current layer.
186238 self ._lbl_status .setText (self ._status [4 ])
187- self ._bar_progress .setMaximum (len (children ) + len (parents ))
239+ if layer :
240+ self ._bar_progress .setMaximum (len (children )* 2 )
241+ else :
242+ self ._bar_progress .setMaximum (len (children ))
188243 progress = 0
189244 for child in children :
190245 child .name = child .parent .name
191246 child .parent = None
192- layer_children .addNode (child )
193247
194248 progress += 1
195249 self ._bar_progress .setValue (progress )
196250
197- for parent in parents :
198- layer_parents .addNode (parent )
251+ if layer :
252+ # Convert the full selection and list of parents to sets, so we can exclude parents from the for-loop
253+ set_selection = set (selection )
254+ set_parents = set (parents )
255+ set_selection = set_selection .difference (set_parents )
256+ current_layer = rt .LayerManager .current
199257
200- progress += 1
258+ progress = len (set_selection )
259+ self ._bar_progress .setMaximum (progress * 2 )
201260 self ._bar_progress .setValue (progress )
261+ for x in set_selection :
262+ progress += 1
263+ current_layer .addNode (x )
264+
265+ self ._bar_progress .setValue (progress )
266+
267+ rt .delete (parents )
202268
203269 # Done.
204270 self ._lbl_status .setText (self ._status [5 ])
205- self ._bar_progress .setValue (self ._bar_progress .maximum ())
271+ self ._bar_progress .setMaximum (1 )
272+ self ._bar_progress .setValue (1 )
273+
274+ # Print some info
275+ print "Cleaned up %d Block/Style Parents" % len (parents )
276+
277+ except Exception :
278+ traceback .print_exc ()
279+ self ._lbl_status .setText (self ._status [7 ])
280+ self ._bar_progress .setMaximum (100 )
281+ self ._bar_progress .setValue (0 )
282+
283+ return
284+
285+ # This should run after Max un-freezes
286+ self ._lbl_status .setText (self ._status [6 ])
206287
207- except Exception as e :
208- print e
209- self ._lbl_status .setText (self ._status [6 ])
210- self ._bar_progress .setMaximum (100 )
211- self ._bar_progress .setValue (0 )
288+ return
212289
213290
214291# --------------------
@@ -224,4 +301,4 @@ def clean(self):
224301ui .show ()
225302
226303# DEBUG
227- # print "\rTest Version 4 "
304+ # print "\rTest Version 7 "
0 commit comments