Skip to content

Commit

Permalink
Enhanced support for scaffold colors
Browse files Browse the repository at this point in the history
- File encoder now saves scaffold oligo colors to a "scaf_colors" dict within relevant the virtual helix.
- Similar to staples, scaffold color information is saved once per oligo, at the idx5p of the strand5p. This also applies to circular oligos (`oligo.isLoop() == True`).
- File decoder will load any saved "scaf_colors" values and restore them upon opening.
- Added keyboard shortcut `Shift+P` to cycle through scaffold color list when the Paint Tool is active.
- The "scaf" selection filter (shortcut key `c`) must be enabled to apply colors to scaffold, and can be disabled to limit coloring to staples.

Bug fixes:
- cadnano.py Removed import of deprecated `imp` package. It was only used for Maya compatibility. This change should prevent import errors in python 3.12, as in issue #52
- legacydecoder: Use QDialogButtonBox.StandardButton enum to avoid crash on unrecognized file format.
  • Loading branch information
sdouglas committed Oct 28, 2023
1 parent 17aa629 commit 82badb9
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 59 deletions.
42 changes: 22 additions & 20 deletions cadnano2/cadnano.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sys, imp
import sys
import os.path
from glob import glob
from code import interact
Expand Down Expand Up @@ -87,23 +87,25 @@ def unloadedPlugins():
results.append(f)
return [x for x in results if x not in loadedPlugins]

def loadPlugin(f):
path, fname = os.path.split(f)
name, ext = os.path.splitext(fname)
pluginKey = os.path.join(path, name)
try:
mod = loadedPlugins[pluginKey]
return mod
except KeyError:
pass
file, filename, data = imp.find_module(name, [path])
mod = imp.load_module(name, file, filename, data)
loadedPlugins[pluginKey] = mod
return mod
# Plugins are no longer supported, so needn't import imp

# def loadPlugin(f):
# path, fname = os.path.split(f)
# name, ext = os.path.splitext(fname)
# pluginKey = os.path.join(path, name)
# try:
# mod = loadedPlugins[pluginKey]
# return mod
# except KeyError:
# pass
# file, filename, data = imp.find_module(name, [path])
# mod = imp.load_module(name, file, filename, data)
# loadedPlugins[pluginKey] = mod
# return mod

def loadAllPlugins():
loadedAPlugin = False
for p in unloadedPlugins():
loadPlugin(p)
loadedAPlugin = True
return loadedAPlugin
# def loadAllPlugins():
# loadedAPlugin = False
# for p in unloadedPlugins():
# loadPlugin(p)
# loadedAPlugin = True
# return loadedAPlugin
13 changes: 10 additions & 3 deletions cadnano2/model/io/legacydecoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def import_legacy_dict(document, obj, latticeType=LatticeType.Honeycomb):
print("Unrecognized file format.")
else:
dialogLT.label.setText("Unrecognized file format.")
dialogLT.buttonBox.setStandardButtons(QDialogButtonBox.Ok)
dialogLT.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Ok)
dialog.exec()

# INSTALL XOVERS
Expand Down Expand Up @@ -234,13 +234,20 @@ def import_legacy_dict(document, obj, latticeType=LatticeType.Honeycomb):
scaf_strand.addInsertion(baseIdx, sumOfInsertSkip, useUndoStack=False)
elif stap_strand:
stap_strand.addInsertion(baseIdx, sumOfInsertSkip, useUndoStack=False)
# end for
# populate colors

# populate staple colors
for baseIdx, colorNumber in helix['stap_colors']:
color = QColor((colorNumber>>16)&0xFF, (colorNumber>>8)&0xFF, colorNumber&0xFF).name()
strand = stapStrandSet.getStrand(baseIdx)
strand.oligo().applyColor(color, useUndoStack=False)

# populate scaffold colors, if any
if 'scaf_colors' in helix:
for baseIdx, colorNumber in helix['scaf_colors']:
color = QColor((colorNumber>>16)&0xFF, (colorNumber>>8)&0xFF, colorNumber&0xFF).name()
strand = scafStrandSet.getStrand(baseIdx)
strand.oligo().applyColor(color, useUndoStack=False)

def isSegmentStartOrEnd(strandType, vhNum, baseIdx, fiveVH, fiveIdx, threeVH, threeIdx):
"""Returns True if the base is a breakpoint or crossover."""
if strandType == StrandType.Scaffold:
Expand Down
12 changes: 9 additions & 3 deletions cadnano2/model/io/legacyencoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ def legacy_dict_from_doc(document, fname, helixOrderList):
insts[idx] = insertion.length()
# colors
stapColors = []
stapStrandSet = vh.stapleStrandSet()
for strand in stapStrandSet:
for strand in vh.stapleStrandSet():
if strand.connection5p() == None:
c = str(strand.oligo().color())[1:] # drop the hash
stapColors.append([strand.idx5Prime(), int(c, 16)])
scafColors = set()
for strand in vh.scaffoldStrandSet():
if strand.connection5p() == None or \
(strand == strand.oligo().strand5p() and strand.oligo().isLoop()):
c = str(strand.oligo().color())[1:] # drop the hash
scafColors.add((strand.idx5Prime(), int(c, 16)))

vhDict = {"row": row,
"col": col,
Expand All @@ -36,7 +41,8 @@ def legacy_dict_from_doc(document, fname, helixOrderList):
"skip": skips,
"scafLoop": [],
"stapLoop": [],
"stap_colors": stapColors}
"stap_colors": stapColors,
"scaf_colors": list(scafColors)}
vhList.append(vhDict)
bname = basename(str(fname))
obj = {"name": bname, "vstrands": vhList}
Expand Down
2 changes: 1 addition & 1 deletion cadnano2/model/oligo.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def undoStack(self):
# end def

### PUBLIC METHODS FOR QUERYING THE MODEL ###
def isLoop(self):
def isLoop(self): # isCircular
return self._isLoop

def isStaple(self):
Expand Down
4 changes: 2 additions & 2 deletions cadnano2/model/strandset.py
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ def __init__(self, strandSet, baseIdxLow, baseIdxHigh, strandSetIdx):
self._strandSet = strandSet
self._sSetIdx = strandSetIdx
self._strand = Strand(strandSet, baseIdxLow, baseIdxHigh)
colorList = styles.stapColors if strandSet.isStaple() else styles.scafColors
colorList = styles.stapColors if strandSet.isStaple() else [styles.scafColors[0]] # default to classic 0066cc
color = random.choice(colorList).name()
self._newOligo = Oligo(None, color) # redo will set part
self._newOligo.setLength(self._strand.totalLength())
Expand Down Expand Up @@ -803,7 +803,7 @@ def __init__(self, strandSet, strand, strandSetIdx, solo=True):
else:
self._newOligo3p = olg3p = olg.shallowCopy()
olg3p.setStrand5p(self._oldStrand3p)
colorList = styles.stapColors if strandSet.isStaple() else styles.scafColors
colorList = styles.stapColors if strandSet.isStaple() else [styles.scafColors[0]]
color = random.choice(colorList).name()
olg3p.setColor(color)
olg3p.refreshLength()
Expand Down
2 changes: 1 addition & 1 deletion cadnano2/ui/mainwindow/ui_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def retranslateUi(self, MainWindow):
self.actionRenumber.setToolTip(_translate("MainWindow", "Renumber Slice helices according to helix ordering in Path panel."))
self.actionPathPaint.setText(_translate("MainWindow", "Paint"))
self.actionPathPaint.setToolTip(_translate("MainWindow", "(P)aint Tool"))
self.actionPathPaint.setShortcut(_translate("MainWindow", "P"))
self.actionPathPaint.setShortcuts([_translate("MainWindow", "P"), _translate("MainWindow", "Shift+P")])
self.actionPathAddSeq.setText(_translate("MainWindow", "Seq"))
self.actionPathAddSeq.setToolTip(_translate("MainWindow", "(A)dd Sequence Tool"))
self.actionPathAddSeq.setShortcut(_translate("MainWindow", "A"))
Expand Down
23 changes: 16 additions & 7 deletions cadnano2/views/pathview/colorpanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import cadnano2.util as util
util.qtWrapImport('QtCore', globals(), ['QRectF', 'Qt'])
util.qtWrapImport('QtGui', globals(), ['QBrush', 'QFont'])
util.qtWrapImport('QtWidgets', globals(), ['QColorDialog',
util.qtWrapImport('QtWidgets', globals(), ['QApplication',
'QColorDialog',
'QGraphicsItem',
'QGraphicsSimpleTextItem'])

Expand All @@ -20,7 +21,7 @@ def __init__(self, parent=None):
self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIgnoresTransformations)
self.colordialog = QColorDialog()
# self.colordialog.setOption(QColorDialog.DontUseNativeDialog)
self._scafColorIndex = -1 # init on -1, painttool will cycle to 0
self._scafColorIndex = 0 # init on -1, painttool will cycle to 0
self._stapColorIndex = -1 # init on -1, painttool will cycle to 0
self._scafColor = self._scafColors[self._scafColorIndex]
self._stapColor = self._stapColors[self._stapColorIndex]
Expand All @@ -47,14 +48,22 @@ def paint(self, painter, option, widget=None):
painter.drawRect(0, 15, 30, 15)

def nextColor(self):
self._stapColorIndex += 1
if self._stapColorIndex == len(self._stapColors):
self._stapColorIndex = 0
self._stapColor = self._stapColors[self._stapColorIndex]
self._stapBrush.setColor(self._stapColor)
if QApplication.keyboardModifiers() & Qt.KeyboardModifier.ShiftModifier:
self._scafColorIndex += 1
if self._scafColorIndex == len(self._scafColors):
self._scafColorIndex = 0
self._scafColor = self._scafColors[self._scafColorIndex]
self._scafBrush.setColor(self._scafColor)
else:
self._stapColorIndex += 1
if self._stapColorIndex == len(self._stapColors):
self._stapColorIndex = 0
self._stapColor = self._stapColors[self._stapColorIndex]
self._stapBrush.setColor(self._stapColor)
self.update()

def prevColor(self):
self._scafColorIndex -= 1
self._stapColorIndex -= 1

def color(self):
Expand Down
7 changes: 4 additions & 3 deletions cadnano2/views/pathview/tools/painttool.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import cadnano2.util as util
util.qtWrapImport('QtCore', globals(), [])
util.qtWrapImport('QtGui', globals(), [])
util.qtWrapImport('QtWidgets', globals(), ['QApplication'])


class PaintTool(AbstractPathTool):
Expand All @@ -27,17 +28,17 @@ def setActive(self, willBeActive):
def widgetClicked(self):
"""Cycle through colors on 'p' keypress"""
self._window.pathColorPanel.nextColor()

def customMouseRelease(self, event):
if self._isMacrod:
self._isMacrod = False
self._window.undoStack().endMacro()
# end def

def isMacrod(self):
return self._isMacrod
# end def

def setMacrod(self):
self._isMacrod = True
self._window.undoStack().beginMacro("Group Paint")
Expand Down
48 changes: 30 additions & 18 deletions cadnano2/views/styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,38 @@
breakfill = QColor(204, 0, 0, 255)
colorbox_fill = QColor(204, 0, 0)
colorbox_stroke = QColor(102, 102, 102)
stapColors = [QColor(204, 0, 0),
QColor(247, 67, 8),
QColor(247, 147, 30),
QColor(170, 170, 0),
QColor(87, 187, 0),
QColor(0, 114, 0),
QColor(3, 182, 162),
QColor(23, 0, 222),
QColor(115, 0, 222),
QColor(184, 5, 108),
QColor(51, 51, 51),
QColor(136, 136, 136)]
scafColors = [QColor(0, 102, 204)]
# QColor(64, 138, 212),
# QColor(0, 38, 76),
# QColor(23, 50, 76),
# QColor(0, 76, 153)]
stapColors = [
QColor(204, 0, 0), #cc0000
QColor(247, 67, 8), #f74308
QColor(247, 147, 30), #f7931e
QColor(170, 170, 0), #aaaa00
QColor( 87, 187, 0), #57bb00
QColor( 0, 114, 0), #007200
QColor( 3, 182, 162), #03b6a2
QColor( 23, 0, 222), #1700de
QColor(115, 0, 222), #7300de
QColor(184, 5, 108), #b8056c
QColor( 51, 51, 51), #333333
QColor(136, 136, 136) #888888
]

scafColors = [
QColor( 0, 102, 204), #0066cc
QColor(102, 0, 0), #990000
QColor(139, 48, 6), #b83006
QColor(198, 125, 23), #c67d17
QColor(136, 136, 0), #888800
QColor( 68, 119, 0), #447700
QColor( 0, 85, 0), #005500
QColor( 15, 0, 183), #0f00b7
QColor( 91, 0, 171), #5b00ab
QColor(157, 5, 108), #9d034f
QColor( 2, 126, 130), #027e82
]

DEFAULT_STAP_COLOR = "#888888"
DEFAULT_SCAF_COLOR = "#0066cc"
selected_color = QColor(255, 51, 51)
selected_color = QColor(255, 51, 51) #ff3333

# brightColors = [QColor() for i in range(10)]
# for i in range(len(brightColors)):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='cadnano2',
version='2.4.10',
version='2.4.11',
description='Cadnano2 for PyQt6',
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down

0 comments on commit 82badb9

Please sign in to comment.