# Copyright 2008 Nanorex, Inc. See LICENSE file for details.
"""
OrderDna_PropertyManager.py
The OrderDna_PropertyManager class provides a Property Manager
for the B{Order Dna} command on the flyout toolbar in the
Build > Dna mode.
@author: Mark
@version: $Id$
@copyright: 2008 Nanorex, Inc. See LICENSE file for details.
"""
import os, time
from widgets.DebugMenuMixin import DebugMenuMixin
from widgets.prefs_widgets import connect_checkbox_with_boolean_pref
from PyQt4.Qt import Qt
from PyQt4.Qt import SIGNAL
from PM.PM_Dialog import PM_Dialog
from PM.PM_GroupBox import PM_GroupBox
from PM.PM_ComboBox import PM_ComboBox
from PM.PM_LineEdit import PM_LineEdit
from PM.PM_PushButton import PM_PushButton
from PM.PM_Constants import PM_DONE_BUTTON
from PM.PM_Constants import PM_WHATS_THIS_BUTTON
from utilities.prefs_constants import assignColorToBrokenDnaStrands_prefs_key
from platform_dependent.PlatformDependent import find_or_make_Nanorex_subdir
from platform_dependent.PlatformDependent import open_file_in_editor
from dna.model.DnaStrand import DnaStrand
#debug flag to keep signals always connected
from utilities.GlobalPreferences import KEEP_SIGNALS_ALWAYS_CONNECTED
def writeDnaOrderFile(fileName, assy, dnaSequence):
"""
Open a temporary file and write the specified Dna sequence into it.
@param fileName: The full path of the temporary file to be opened
@param assy: The assembly.
@param dnaSequence: The dnaSequence string to be written to the file.
@see: self.orderDna
"""
#Create Header
headerString = '#NanoEngineer-1 DNA Order Form created on: '
timestr = "%s\n" % time.strftime("%Y-%m-%d at %H:%M:%S")
if assy.filename:
mmpFileName = "[" + os.path.normpath(assy.filename) + "]"
else:
mmpFileName = "[" + assy.name + "]" + \
" ( The mmp file was probably not saved when the "\
" sequence was written)"
fileNameInfo_header = "#This sequence is created for file '%s\n\n'" \
% mmpFileName
headerString = headerString + timestr + fileNameInfo_header
f = open(fileName,'w')
# Write header
f.write(headerString)
f.write("Name,Sequence,Notes\n") # Per IDT's Excel format.
f.write(dnaSequence)
class OrderDna_PropertyManager( PM_Dialog, DebugMenuMixin ):
"""
The OrderDna_PropertyManager class provides a Property Manager
for the B{Order Dna} command on the flyout toolbar in the
Build > Dna mode.
@ivar title: The title that appears in the property manager header.
@type title: str
@ivar pmName: The name of this property manager. This is used to set
the name of the PM_Dialog object via setObjectName().
@type name: str
@ivar iconPath: The relative path to the PNG file that contains a
22 x 22 icon image that appears in the PM header.
@type iconPath: str
"""
title = "Order Dna"
pmName = title
iconPath = "ui/actions/Command Toolbar/Order_DNA.png"
def __init__( self, command ):
"""
Constructor for the property manager.
"""
self.command = command
self.w = self.command.w
self.win = self.command.w
self.pw = self.command.pw
self.o = self.win.glpane
self.assy = self.win.assy
PM_Dialog.__init__(self, self.pmName, self.iconPath, self.title)
DebugMenuMixin._init1( self )
self.showTopRowButtons( PM_DONE_BUTTON | \
PM_WHATS_THIS_BUTTON)
self.update_includeStrands() # Updates the message box.
if KEEP_SIGNALS_ALWAYS_CONNECTED:
self.connect_or_disconnect_signals(True)
##if self.getNumberOfBases():
##msg = "Click on View DNA Order File... to preview a "\
##"DNA order for all DNA strands in the current model."
##else:
##msg = "There is no DNA in the current model."
##self.updateMessage(msg)
def connect_or_disconnect_signals(self, isConnect):
"""
Connect or disconnect widget signals sent to their slot methods.
This can be overridden in subclasses. By default it does nothing.
@param isConnect: If True the widget will send the signals to the slot
method.
@type isConnect: boolean
"""
if isConnect:
change_connect = self.win.connect
else:
change_connect = self.win.disconnect
change_connect( self.viewDnaOrderFileButton,
SIGNAL("clicked()"),
self.viewDnaOrderFile)
change_connect( self.includeStrandsComboBox,
SIGNAL("activated(int)"),
self.update_includeStrands )
def show(self):
"""
Shows the Property Manager. Overrides PM_Dialog.show.
"""
PM_Dialog.show(self)
if not KEEP_SIGNALS_ALWAYS_CONNECTED:
self.connect_or_disconnect_signals(True)
def close(self):
"""
Closes the Property Manager. Overrides PM_Dialog.close.
"""
if not KEEP_SIGNALS_ALWAYS_CONNECTED:
self.connect_or_disconnect_signals(False)
PM_Dialog.close(self)
def _addGroupBoxes( self ):
"""
Add the Property Manager group boxes.
"""
self._pmGroupBox1 = PM_GroupBox( self, title = "Options" )
self._loadGroupBox1( self._pmGroupBox1 )
def _loadGroupBox1(self, pmGroupBox):
"""
Load widgets in group box.
"""
includeStrandsChoices = ["All strands in model",
"Selected strands only"]
self.includeStrandsComboBox = \
PM_ComboBox( pmGroupBox,
label = "Include strands:",
choices = includeStrandsChoices,
setAsDefault = True)
self.numberOfBasesLineEdit = \
PM_LineEdit( pmGroupBox,
label = "Number of bases:",
text = str(self.getNumberOfBases()))
self.numberOfBasesLineEdit.setEnabled(False)
self.viewDnaOrderFileButton = \
PM_PushButton( pmGroupBox,
label = "",
text = "View DNA Order File...",
spanWidth = True)
def _addWhatsThisText( self ):
"""
What's This text for widgets in the DNA Property Manager.
"""
pass
def _addToolTipText(self):
"""
Tool Tip text for widgets in the DNA Property Manager.
"""
pass
# Ask Bruce where this should live (i.e. class Part?) --Mark
def getAllDnaStrands(self, selectedOnly = False):
"""
Returns a list of all the DNA strands in the current part, or only
the selected strands if I{selectedOnly} is True.
@param selectedOnly: If True, return only the selected DNA strands.
@type selectedOnly: bool
"""
dnaStrandList = []
def func(node):
if isinstance(node, DnaStrand):
if selectedOnly:
if node.picked:
dnaStrandList.append(node)
else:
dnaStrandList.append(node)
self.win.assy.part.topnode.apply2all(func)
return dnaStrandList
def getNumberOfBases(self, selectedOnly = False):
"""
Returns the number of bases count for all the DNA strands in the
current part, or only the selected strand if I{selectedOnly} is True.
@param selectedOnly: If True, return only the selected DNA strands.
@type selectedOnly: bool
"""
dnaSequenceString = ''
selectedOnly = self.includeStrandsComboBox.currentIndex()
strandList = self.getAllDnaStrands(selectedOnly)
for strand in strandList:
strandSequenceString = str(strand.getStrandSequence())
dnaSequenceString += strandSequenceString
return len(dnaSequenceString)
def getDnaSequence(self, format = 'CSV'):
"""
Return the complete Dna sequence information string (i.e. all strand
sequences) in the specified format.
@return: The Dna sequence string
@rtype: string
"""
if format == 'CSV': #comma separated values.
separator = ','
dnaSequenceString = ''
selectedOnly = self.includeStrandsComboBox.currentIndex()
strandList = self.getAllDnaStrands(selectedOnly)
for strand in strandList:
dnaSequenceString = dnaSequenceString + strand.name + separator
strandSequenceString = str(strand.getStrandSequence())
if strandSequenceString:
strandSequenceString = strandSequenceString.upper()
dnaSequenceString = dnaSequenceString + strandSequenceString
dnaSequenceString = dnaSequenceString + "\n"
return dnaSequenceString
def viewDnaOrderFile(self, openFileInEditor = True):
"""
Opens a text editor and loads a temporary text file containing all the
DNA strand names and their sequences in the current DNA object. It will
look something like this:
Strand1,ATCAGCTACGCATCGCT
Strand2,TAGTCGATGCGTAGCGA
...
Strandn, ...
The user can then save the file to a permanent location using the
text editor the file is loaded (and displayed) in.
@see: Ui_DnaFlyout.orderDnaCommand
@see: writeDnaOrderFile()
@TODO: assy.getAllDnaObjects().
"""
dnaSequence = self.getDnaSequence(format = 'CSV')
if dnaSequence:
tmpdir = find_or_make_Nanorex_subdir('temp')
fileBaseName = 'DnaOrder'
temporaryFile = os.path.join(tmpdir, "%s.csv" % fileBaseName)
writeDnaOrderFile(temporaryFile,
self.assy,
dnaSequence)
if openFileInEditor:
open_file_in_editor(temporaryFile)
def update_includeStrands(self, ignoreVal = 0):
"""
Slot method for "Include (strands)" combobox.
"""
idx = self.includeStrandsComboBox.currentIndex()
includeType = ["model", "selection"]
_numberOfBases = self.getNumberOfBases()
self.numberOfBasesLineEdit.setText(str(_numberOfBases))
if _numberOfBases > 0:
self.viewDnaOrderFileButton.setEnabled(True)
msg = "Click on View DNA Order File... to preview a " \
"DNA order for all DNA strands in the current %s." \
% includeType[idx]
else:
self.viewDnaOrderFileButton.setEnabled(False)
msg = "" \
"There are no DNA strands in the current %s." \
% includeType[idx]
self.updateMessage(msg)