1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
|
# Copyright 2007-2008 Nanorex, Inc. See LICENSE file for details.
"""
BuildDna_EditCommand.py
@author: Ninad
@version: $Id$
@copyright: 2007-2008 Nanorex, Inc. See LICENSE file for details.
History:
Ninad 2008-01-11: Created
TODO: as of 2008-01-11
- Needs more documentation and the file is subjected to heavy revision.
This is an initial implementation of default Dna edit mode.
- Methods such as callback_addSegments might be renamed.
BUGS:
- Has bugs such as -- Flyout toolbar doesn't get updated when you return to
BuildDna_EditCommand from a a temporary command.
- Just entering and leaving BuilddDna_EditCommand creates an empty DnaGroup
"""
from command_support.EditCommand import EditCommand
from dna.model.DnaGroup import DnaGroup
from utilities.Log import greenmsg
from utilities.exception_classes import PluginBug, UserError
from utilities.constants import gensym
from ne1_ui.toolbars.Ui_DnaFlyout import DnaFlyout
from model.chem import Atom
from model.chunk import Chunk
from model.bonds import Bond
from dna.commands.BuildDna.BuildDna_GraphicsMode import BuildDna_GraphicsMode
from dna.commands.BuildDna.BuildDna_PropertyManager import BuildDna_PropertyManager
class BuildDna_EditCommand(EditCommand):
"""
BuildDna_EditCommand provides a convenient way to edit or create
a DnaGroup object
"""
#Temporary attr 'command_porting_status. See baseCommand for details.
command_porting_status = None
#GraphicsMode
GraphicsMode_class = BuildDna_GraphicsMode
#Property Manager
PM_class = BuildDna_PropertyManager
cmd = greenmsg("Build DNA: ")
prefix = 'DnaGroup' # used for gensym
cmdname = "Build Dna"
commandName = 'BUILD_DNA'
featurename = "Build Dna"
from utilities.constants import CL_ENVIRONMENT_PROVIDING
command_level = CL_ENVIRONMENT_PROVIDING
command_should_resume_prevMode = False
command_has_its_own_PM = True
command_can_be_suspended = True
# Generators for DNA, nanotubes and graphene have their MT name
# generated (in GeneratorBaseClass) from the prefix.
create_name_from_prefix = True
#The following class constant is used in creating dynamic menu items (using self.makeMenus)
#if this flag is not defined, the menu doesn't get created
#or use of self.graphicsMode in self.makeMenus throws errors.
#See also other examples of its use in older Commands such as
#BuildAtoms_Command (earlier depositmode)
call_makeMenus_for_each_event = True
def __init__(self, commandSequencer, struct = None):
"""
Constructor for BuildDna_EditCommand
"""
EditCommand.__init__(self, commandSequencer)
self.struct = struct
#=== START NEW COMMAND API methods ======================================
#Used in self.init_gui and self.restore_gui as of 2008-08-11
def command_enter_flyout(self):
"""
Overrides superclass method.
@see: EditCommand.command_enter_flyout()
"""
if self.flyoutToolbar is None:
self.flyoutToolbar = self._createFlyoutToolBarObject()
self.flyoutToolbar.activateFlyoutToolbar()
def command_exit_flyout(self):
"""
Overrides superclass method.
@see: EditCommand.command_exit_flyout()
"""
if self.flyoutToolbar:
self.flyoutToolbar.deActivateFlyoutToolbar()
def _createFlyoutToolBarObject(self):
"""
Create a flyout toolbar to be shown when this command is active.
Overridden in subclasses.
@see: PasteFromClipboard_Command._createFlyouttoolBar()
@see: self.command_enter_flyout()
"""
flyoutToolbar = DnaFlyout(self)
return flyoutToolbar
#=== END NEW COMMAND API methods ========================================
def resume_gui(self):
"""
Called when this command, that was suspended earlier, is being resumed.
The temporary command (which was entered by suspending this command)
might have made some changes to the model which need to be reflected
while resuming command.
Example: A user enters BreakStrands_Command by suspending
BuildDna_EditCommand, then breaks a few strands, thereby creating new
strand chunks. Now when the user returns to the BuildDna_EditCommand,
the command's property manager needs to update the list of strands
because of the changes done while in BreakStrands_Command.
@see: Command.resume_gui
@see: Command._enterMode where this method is called.
"""
#NOTE: Doing command toolbar updates in this method doesn't alwayswork.
#consider this situation : You are in a) BuildDna_EditCommand, then you
#b) enter DnaDuplex_EditCommand(i.e. Dna line) and from this temporary
#command, you directly c) enter BreakStrands_Command
#-- During b to c, 1) it first exits (b) , 2) resumes (a)
#and then 3)enters (c)
#This method is called during operation #2 and any changes to flyout
#toolbar are reset during #3 --- Ninad 2008-01-14
if self.propMgr:
self.propMgr.updateListWidgets()
if self.flyoutToolbar:
self.flyoutToolbar.resetStateOfActions()
def runCommand(self):
"""
Overrides EditCommand.runCommand
"""
self.struct = None
self.existingStructForEditing = False
self.propMgr.updateListWidgets()
def keep_empty_group(self, group):
"""
Returns True if the empty group should not be automatically deleted.
otherwise returns False. The default implementation always returns
False. Subclasses should override this method if it needs to keep the
empty group for some reasons. Note that this method will only get called
when a group has a class constant autdelete_when_empty set to True.
(and as of 2008-03-06, it is proposed that dna_updater calls this method
when needed.
@see: Command.keep_empty_group() which is overridden here.
"""
bool_keep = EditCommand.keep_empty_group(self, group)
if not bool_keep:
if group is self.struct:
bool_keep = True
return bool_keep
def create_and_or_show_PM_if_wanted(self, showPropMgr = True):
"""
Create the property manager object if one doesn't already exist
and then show the propMgr if wanted by the user.
@param showPropMgr: If True, show the property manager
@type showPropMgr: boolean
"""
EditCommand.create_and_or_show_PM_if_wanted(
self,
showPropMgr = showPropMgr)
self.propMgr.updateMessage("Use appropriate command in the command "\
"toolbar to create or modify a DNA Object"\
"<br>"
)
def createStructure(self, showPropMgr = True):
"""
Overrides superclass method. It doesn't do anything for this type
of editcommand
"""
self.preview_or_finalize_structure(previewing = True)
def editStructure(self, struct = None):
"""
Overrides EditCommand.editStructure method. Provides a way to edit an
existing structure. This implements a topLevel command that the client
can execute to edit an existing object(i.e. self.struct) that it wants.
Example: If its a plane edit controller, this method will be used to
edit an object of class Plane.
This method also creates a propMgr objects if it doesn't exist and
shows this property manager
@see: L{self.createStructure} (another top level command that
facilitates creation of a model object created by this
editCommand
@see: L{Plane.edit} and L{Plane_EditCommand._createPropMgrObject}
"""
if struct is not None:
#Should we always unpick the structure while editing it?
#Makes sense for editing a Dna. If this is problematic, the
#following should be done in the subclasses that need this.
if hasattr(struct, 'picked') and struct.picked:
struct.unpick()
EditCommand.editStructure(self, struct)
def _getStructureType(self):
"""
Subclasses override this method to define their own structure type.
Returns the type of the structure this editCommand supports.
This is used in isinstance test.
@see: EditCommand._getStructureType() (overridden here)
@see: self.hasValidStructure()
"""
return self.win.assy.DnaGroup
def _createStructure(self):
"""
creates and returns the structure (in this case a L{Group} object that
contains the DNA strand and axis chunks.
@return : group containing that contains the DNA strand and axis chunks.
@rtype: L{Group}
@note: This needs to return a DNA object once that model is implemented
"""
# self.name needed for done message
if self.create_name_from_prefix:
# create a new name
name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
self._gensym_data_for_reusing_name = (self.prefix, name)
else:
# use externally created name
self._gensym_data_for_reusing_name = None
# (can't reuse name in this case -- not sure what prefix it was
# made with)
name = self.name
# Create the model tree group node.
# Make sure that the 'topnode' of this part is a Group (under which the
# DNa group will be placed), if the topnode is not a group, make it a
# a 'Group' (applicable to Clipboard parts).See part.py
# --Part.ensure_toplevel_group method. This is an important line
# and it fixes bug 2585
self.win.assy.part.ensure_toplevel_group()
dnaGroup = DnaGroup(self.name,
self.win.assy,
self.win.assy.part.topnode,
editCommand = self)
try:
self.win.assy.place_new_geometry(dnaGroup)
return dnaGroup
except (PluginBug, UserError):
# Why do we need UserError here? Mark 2007-08-28
dnaGroup.kill()
raise PluginBug("Internal error while trying to create DNA duplex.")
def _gatherParameters(self):
"""
Return the parameters needed to build this structure
@return: A list of all DnaSegments present withing the self.struct
(which is a dna group) or None if self.structure doesn't exist
@rtype: tuple
"""
#Passing the segmentList as a parameter is not implemented
##if self.struct:
##segmentList = []
##for segment in self.struct.members:
##if isinstance(segment, DnaSegment):
##segmentList.append(segment)
##if segmentList:
##return (segmentList)
return None
def _modifyStructure(self, params):
"""
Modify the structure based on the parameters specified.
Overrides EditCommand._modifystructure. This method removes the old
structure and creates a new one using self._createStructure. This
was needed for the structures like this (Dna, Nanotube etc) . .
See more comments in the method.
"""
assert self.struct
# parameters have changed, update existing structure
self._revertNumber()
# self.name needed for done message
if self.create_name_from_prefix:
# create a new name
name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
self._gensym_data_for_reusing_name = (self.prefix, name)
else:
# use externally created name
self._gensym_data_for_reusing_name = None
# (can't reuse name in this case -- not sure what prefix it was
# made with)
name = self.name
#@NOTE: Unlike editcommands such as Plane_EditCommand, this
#editCommand actually removes the structure and creates a new one
#when its modified. We don't yet know if the DNA object model
# will solve this problem. (i.e. reusing the object and just modifying
#its attributes. Till that time, we'll continue to use
#what the old GeneratorBaseClass use to do ..i.e. remove the item and
# create a new one -- Ninad 2007-10-24
self._removeStructure()
self.previousParams = params
self.struct = self._createStructure()
return
def _finalizeStructure(self):
"""
Overrides EditCommand._finalizeStructure.
This method also makes sure that the DnaGroup is not empty ..if its
empty it deletes it.
@see: dna_model.DnaGroup.isEmpty
@see: EditCommand.preview_or_finalize_structure
"""
if self.struct is not None:
if self.struct.isEmpty():
#Don't keep empty DnaGroup Fixes bug 2603.
self._removeStructure()
self.win.win_update()
else:
EditCommand._finalizeStructure(self)
if self.struct is not None:
#Make sure that he DnaGroup in the Model Tree is in collapsed state
#after finalizing the structure.
#DON'T DO self.struct.open = False in the above conditional
#because the EditCommand._finalizeStructure may have assigned
#'None' for 'self.struct'!
self.struct.open = False
def cancelStructure(self):
"""
Cancel the structure
"""
EditCommand.cancelStructure(self)
if self.struct is not None:
if self.struct.isEmpty():
self._removeStructure()
def provideParamsForTemporaryMode_in_BuildDna(self, temporaryModeName = None):
# REVIEW: this is called directly by our subcommand DnaDuplex_EditCommand.
# I'm not sure if it's ever called by a request command
# (but I guess not, due to its old-code check on
# temporaryModeName == 'DNA_DUPLEX').
# If not, it should be removed in favor of direct access to
# methods or attrs of interest. See comment near its call in
# DnaDuplex_EditCommand.py.
#
# For now, I just renamed it and its call, to verify this theory.
# Later it should be renamed better and its argument removed.
# [bruce 080801 comment]
"""
## NOTE: This needs to be a general API method. There are situations when
## user enters a temporary mode , does something there and returns back to
## the previous mode he was in. He also needs to send some data from
## previous mode to the temporary mode .
## @see: B{DnaLineMode}
## @see: self.acceptParamsFromTemporaryMode
@see DnaDuplex_EditCommand._createSegment(),
@see: DnaDuplex_EditCommand.createStructure()
@see: DnaDuplex_EditCommand.restore_gui()
"""
params = ()
if temporaryModeName in ('DNA_DUPLEX', None):
#Pass the self.struct to the DnaDuplex_EdiCommand
#This deprecates use of self.callback_addSegments (in which
#segments created while in DnaDuplex command are added after
#returning to BuildDna mode) The new implementation provides the
#DnaGroup to the DnaDuplex command and then adds the created
#segments directly to it.
#See: DnaDuplex_EditCommand._createSegment(),
# DnaDuplex_EditCommand.createStructure(), and
# DnaDuplex_EditCommand.restore_gui()
#following condition (hasValidStructure) fixes bug 2815.Earlier
#ondition was just checking if self.struct is None.
#self.hasValidStructure checks if the structure is killed etc
#--Ninad 2008-04-21
if not self.hasValidStructure():
self.struct = self._createStructure()
params = (self.struct)
return params
def makeMenus(self):
"""
Create context menu for this command. (Build Dna mode)
@see: chunk.make_glpane_context_menu_items
@see: DnaSegment_EditCommand.makeMenus
"""
if not hasattr(self, 'graphicsMode'):
return
selobj = self.glpane.selobj
if selobj is None:
self._makeEditContextMenus()
return
self.Menu_spec = []
highlightedChunk = None
if isinstance(selobj, Chunk):
highlightedChunk = selobj
if isinstance(selobj, Atom):
highlightedChunk = selobj.molecule
elif isinstance(selobj, Bond):
chunk1 = selobj.atom1.molecule
chunk2 = selobj.atom2.molecule
if chunk1 is chunk2 and chunk1 is not None:
highlightedChunk = chunk1
if highlightedChunk is not None:
highlightedChunk.make_glpane_context_menu_items(self.Menu_spec,
command = self)
return
|