Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Open sidebar
GEPARD
GEPARD
Commits
672f17f6
Commit
672f17f6
authored
Sep 08, 2020
by
Josef Brandt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
From Linux..
parent
f3c17ebf
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
573 additions
and
330 deletions
+573
-330
__main__.py
__main__.py
+19
-19
analysis/particleAndMeasurement.py
analysis/particleAndMeasurement.py
+6
-1
analysis/particleCharacterization.py
analysis/particleCharacterization.py
+61
-36
analysis/particleClassification/shapeClassification.py
analysis/particleClassification/shapeClassification.py
+1
-0
analysis/particleContainer.py
analysis/particleContainer.py
+102
-38
analysis/particleEditor.py
analysis/particleEditor.py
+94
-97
analysis/particlePainter.py
analysis/particlePainter.py
+47
-41
dataset.py
dataset.py
+132
-60
gui/detectionview.py
gui/detectionview.py
+2
-2
gui/specscanui.py
gui/specscanui.py
+4
-15
gui/viewItemHandler.py
gui/viewItemHandler.py
+11
-0
gui/viewItems/detectItems.py
gui/viewItems/detectItems.py
+34
-4
helperfunctions.py
helperfunctions.py
+7
-3
legacyConvert.py
legacyConvert.py
+29
-8
sampleview.py
sampleview.py
+5
-1
scenePyramid.py
scenePyramid.py
+3
-3
unittests/test_gepard.py
unittests/test_gepard.py
+14
-2
zeissimporter.py
zeissimporter.py
+2
-0
No files found.
__main__.py
View file @
672f17f6
...
...
@@ -36,6 +36,25 @@ from .unittests.test_gepard import testGepard
from
.helperfunctions
import
getAppFolder
def
excepthook
(
excType
,
excValue
,
tracebackobj
):
"""
Global function to catch unhandled exceptions.
@param excType exception type
@param excValue exception value
@param tracebackobj traceback object
:return:
"""
tbinfofile
=
StringIO
()
traceback
.
print_tb
(
tracebackobj
,
None
,
tbinfofile
)
tbinfofile
.
seek
(
0
)
tbinfo
=
tbinfofile
.
read
()
logging
.
critical
(
"Fatal error in program excecution!"
)
logging
.
critical
(
tbinfo
)
from
.errors
import
showErrorMessageAsWidget
showErrorMessageAsWidget
(
tbinfo
)
class
GEPARDMainWindow
(
QtWidgets
.
QMainWindow
):
def
__init__
(
self
,
logger
):
super
(
GEPARDMainWindow
,
self
).
__init__
()
...
...
@@ -387,25 +406,6 @@ if __name__ == '__main__':
"""
app
.
closeAllWindows
()
def
excepthook
(
excType
,
excValue
,
tracebackobj
):
"""
Global function to catch unhandled exceptions.
@param excType exception type
@param excValue exception value
@param tracebackobj traceback object
:return:
"""
tbinfofile
=
StringIO
()
traceback
.
print_tb
(
tracebackobj
,
None
,
tbinfofile
)
tbinfofile
.
seek
(
0
)
tbinfo
=
tbinfofile
.
read
()
logging
.
critical
(
"Fatal error in program excecution!"
)
logging
.
critical
(
tbinfo
)
from
.errors
import
showErrorMessageAsWidget
showErrorMessageAsWidget
(
tbinfo
)
sys
.
excepthook
=
excepthook
logger
=
logging
.
getLogger
(
__name__
)
...
...
analysis/particleAndMeasurement.py
View file @
672f17f6
...
...
@@ -20,6 +20,8 @@ If not, see <https://www.gnu.org/licenses/>.
"""
import
numpy
as
np
from
typing
import
List
,
TYPE_CHECKING
# from .. import dataset
from
gepard
import
dataset
if
TYPE_CHECKING
:
from
.particleCharacterization
import
FTIRAperture
...
...
@@ -28,6 +30,9 @@ if TYPE_CHECKING:
class
Particle
(
object
):
def
__init__
(
self
):
super
(
Particle
,
self
).
__init__
()
# from ...gepard.dataset import DataSet
self
.
uid
:
int
=
dataset
.
DataSet
.
getUID
()
self
.
index
:
int
=
np
.
nan
self
.
longSize
:
float
=
np
.
nan
self
.
shortSize
:
float
=
np
.
nan
...
...
@@ -50,7 +55,7 @@ class Particle(object):
meas
.
setHQI
(
100
)
def
getParticleAssignment
(
self
)
->
str
:
assignment
=
'
Will not be measured
'
assignment
=
'
Excluded from Spectrum Scan
'
if
len
(
self
.
measurements
)
>
0
:
assignment
=
self
.
getMeasAssignmentWithHighestHQI
()
return
assignment
...
...
analysis/particleCharacterization.py
View file @
672f17f6
...
...
@@ -94,9 +94,7 @@ def getFTIRAperture(partImg: np.ndarray) -> FTIRAperture:
return
aperture
def
getParticleStatsWithPixelScale
(
contour
,
dataset
,
fullimage
=
None
,
zimg
=
None
,
ftir
:
bool
=
False
):
if
fullimage
is
None
:
fullimage
=
loadFullimageFromDataset
(
dataset
)
def
getParticleStatsWithPixelScale
(
contour
,
dataset
,
fullimage
=
None
,
scenePyramid
=
None
,
zimg
=
None
,
ftir
:
bool
=
False
):
if
zimg
is
None
:
zimg
=
loadZValImageFromDataset
(
dataset
)
...
...
@@ -119,7 +117,12 @@ def getParticleStatsWithPixelScale(contour, dataset, fullimage=None, zimg=None,
newStats
.
longSize
*=
pixelscale
newStats
.
shortSize
*=
pixelscale
partImg
,
extrema
=
getParticleImageFromFullimage
(
cnt
,
fullimage
)
partImg
=
None
if
scenePyramid
is
None
and
fullimage
is
not
None
:
partImg
,
extrema
=
getParticleImageFromFullimage
(
cnt
,
fullimage
)
elif
scenePyramid
is
not
None
and
fullimage
is
None
:
partImg
,
extrema
=
getParticleImageFromScenePyramid
(
cnt
,
scenePyramid
)
assert
partImg
is
not
None
,
"error in getting particle image"
newStats
.
color
=
getParticleColor
(
partImg
)
if
ftir
:
...
...
@@ -168,7 +171,7 @@ def getParticleHeight(contour, fullZimg, dataset):
zimg
=
cv2
.
medianBlur
(
zimg
,
5
)
avg_ZValue
=
np
.
mean
(
zimg
[
zimg
>
0
])
if
np
.
isnan
(
avg_ZValue
):
#i.e., only zeros in zimg
if
np
.
isnan
(
avg_ZValue
):
#
i.e., only zeros in zimg
avg_ZValue
=
0
z0
,
z1
=
dataset
.
zpositions
.
min
(),
dataset
.
zpositions
.
max
()
height
=
avg_ZValue
/
255.
*
(
z1
-
z0
)
+
z0
...
...
@@ -176,20 +179,20 @@ def getParticleHeight(contour, fullZimg, dataset):
def
getContourStats
(
cnt
):
#
#
characterize particle
if
cnt
.
shape
[
0
]
>=
5
:
#
#at least 5 points required for ellipse fitting...
ellipse
=
cv2
.
fitEllipse
(
cnt
)
short
,
long
=
ellipse
[
1
]
else
:
rect
=
cv2
.
minAreaRect
(
cnt
)
long
,
short
=
rect
[
1
]
if
short
>
long
:
long
,
short
=
short
,
long
area
=
cv2
.
contourArea
(
cnt
)
return
long
,
short
,
area
#
characterize particle
if
cnt
.
shape
[
0
]
>=
5
:
#
at least 5 points required for ellipse fitting...
ellipse
=
cv2
.
fitEllipse
(
cnt
)
short
,
long
=
ellipse
[
1
]
else
:
rect
=
cv2
.
minAreaRect
(
cnt
)
long
,
short
=
rect
[
1
]
if
short
>
long
:
long
,
short
=
short
,
long
area
=
cv2
.
contourArea
(
cnt
)
return
long
,
short
,
area
def
mergeContours
(
contours
):
...
...
@@ -217,11 +220,30 @@ def getParticleImageFromFullimage(contour, fullimage):
return
img
,
(
xmin
,
xmax
,
ymin
,
ymax
)
def
getParticleImageFromScenePyramid
(
contour
,
scenePyramid
):
contourCopy
=
deepcopy
(
contour
)
xmin
,
xmax
,
ymin
,
ymax
=
getContourExtrema
(
contourCopy
)
img
=
scenePyramid
.
getImagePart
(
ymin
,
ymax
,
xmin
,
xmax
)
img
=
img
.
copy
()
mask
=
np
.
zeros
(
img
.
shape
[:
2
])
for
i
in
range
(
len
(
contourCopy
)):
contourCopy
[
i
][
0
][
0
]
-=
xmin
contourCopy
[
i
][
0
][
1
]
-=
ymin
cv2
.
drawContours
(
mask
,
[
contourCopy
],
-
1
,
(
255
,
255
,
255
),
-
1
)
cv2
.
drawContours
(
mask
,
[
contourCopy
],
-
1
,
(
255
,
255
,
255
),
1
)
img
[
mask
==
0
]
=
0
img
=
np
.
array
(
img
,
dtype
=
np
.
uint8
)
return
img
,
(
xmin
,
xmax
,
ymin
,
ymax
)
def
contoursToImg
(
contours
,
padding
=
0
):
contourCopy
=
deepcopy
(
contours
)
xmin
,
xmax
,
ymin
,
ymax
=
getContourExtrema
(
contourCopy
)
padding
=
padding
#pixel in each direction
rangex
=
int
(
np
.
round
((
xmax
-
xmin
)
+
2
*
padding
))
rangey
=
int
(
np
.
round
((
ymax
-
ymin
)
+
2
*
padding
))
if
rangex
==
0
or
rangey
==
0
:
...
...
@@ -239,36 +261,39 @@ def contoursToImg(contours, padding=0):
def
imgToCnt
(
img
,
xmin
,
ymin
,
padding
=
0
):
def
getContour
(
img
,
contourMode
):
def
getLargestContour
(
contours
):
areas
=
[]
for
contour
in
contours
:
areas
.
append
(
cv2
.
contourArea
(
contour
))
maxIndex
=
areas
.
index
(
max
(
areas
))
print
(
f
'
{
len
(
contours
)
}
contours found, getting the largest one. Areas are:
{
areas
}
, '
f
'taking contour at index
{
maxIndex
}
'
)
return
contours
[
maxIndex
]
if
cv2
.
__version__
>
'3.5'
:
contours
,
hierarchy
=
cv2
.
findContours
(
img
,
cv2
.
RETR_EXTERNAL
,
contourMode
)
else
:
temp
,
contours
,
hierarchy
=
cv2
.
findContours
(
img
,
cv2
.
RETR_EXTERNAL
,
contourMode
)
if
len
(
contours
)
==
0
:
#i.e., no contour found
if
len
(
contours
)
==
0
:
#
i.e., no contour found
raise
InvalidParticleError
elif
len
(
contours
)
==
1
:
#i.e., exactly one contour found
elif
len
(
contours
)
==
1
:
#
i.e., exactly one contour found
contour
=
contours
[
0
]
else
:
#i.e., multiple contours found
else
:
#
i.e., multiple contours found
contour
=
getLargestContour
(
contours
)
return
contour
def
getLargestContour
(
contours
):
areas
=
[]
for
contour
in
contours
:
areas
.
append
(
cv2
.
contourArea
(
contour
))
maxIndex
=
areas
.
index
(
max
(
areas
))
print
(
f
'
{
len
(
contours
)
}
contours found, getting the largest one. Areas are:
{
areas
}
, taking contour at index
{
maxIndex
}
'
)
return
contours
[
maxIndex
]
img
=
closeHolesOfSubImage
(
img
)
contour
=
getContour
(
img
,
contourMode
=
cv2
.
CHAIN_APPROX_NONE
)
for
i
in
range
(
len
(
contour
)):
contour
[
i
][
0
][
0
]
+=
xmin
-
padding
contour
[
i
][
0
][
1
]
+=
ymin
-
padding
contour
[
i
][
0
][
0
]
+=
xmin
-
padding
contour
[
i
][
0
][
1
]
+=
ymin
-
padding
return
contour
...
...
analysis/particleClassification/shapeClassification.py
View file @
672f17f6
...
...
@@ -22,6 +22,7 @@ If not, see <https://www.gnu.org/licenses/>.
import
cv2
from
...errors
import
InvalidParticleError
class
ShapeClassifier
(
object
):
def
__init__
(
self
):
self
.
shapeClasses
=
[
Spherule
(),
Fibre
(),
Irregular
()]
...
...
analysis/particleContainer.py
View file @
672f17f6
...
...
@@ -24,20 +24,41 @@ import os
from
PyQt5
import
QtWidgets
from
typing
import
List
,
TYPE_CHECKING
from
.
import
importSpectra
from
.particleAndMeasurement
import
Particle
,
Measurement
specImportEnabled
:
bool
=
True
try
:
from
..gepardevaluation.analysis
import
importSpectra
except
ModuleNotFoundError
:
specImportEnabled
=
False
if
TYPE_CHECKING
:
from
..dataset
import
DataSet
class
ParticleContainer
(
object
):
def
__init__
(
self
,
datasetParent
:
'DataSet'
):
def
__init__
(
self
):
super
(
ParticleContainer
,
self
).
__init__
()
self
.
datasetParent
:
'DataSet'
=
datasetParent
self
.
datasetParent
:
'DataSet'
=
None
self
.
particles
:
List
[
Particle
]
=
[]
self
.
measurements
:
List
[
Measurement
]
=
[]
self
.
inconsistentParticles
:
List
[
Particle
]
=
[]
def
setDataSet
(
self
,
ds
):
"""
dynamically sets a reference to the dataset containing this particle container
:param ds:
:return:
"""
self
.
datasetParent
=
ds
def
unsetDataSet
(
self
):
"""
is called before dataset is saved, preventing pickle to resolve this reference to dataset into
a copy of it (baking its data into the save file)
:return:
"""
self
.
datasetParent
=
None
def
addEmptyMeasurement
(
self
)
->
int
:
newMeas
=
Measurement
()
self
.
measurements
.
append
(
newMeas
)
...
...
@@ -58,33 +79,30 @@ class ParticleContainer(object):
self
.
measurements
[
indexOfMeasurment
].
pixelcoord_x
=
x
self
.
measurements
[
indexOfMeasurment
].
pixelcoord_y
=
y
def
getMeasurementScanindex
(
self
,
indexOfMeasurement
):
def
getMeasurementScanindex
(
self
,
indexOfMeasurement
)
->
int
:
return
self
.
measurements
[
indexOfMeasurement
].
getScanIndex
()
def
getSpectraFromDisk
(
self
):
def
getSpectraFromDisk
(
self
)
->
np
.
ndarray
:
spectra
=
None
specPath
=
self
.
datasetParent
.
getSpectraFileName
()
if
os
.
path
.
exists
(
specPath
):
spectra
=
np
.
load
(
specPath
)
else
:
fname
=
QtWidgets
.
QFileDialog
.
getOpenFileName
(
QtWidgets
.
QWidget
(),
'Select Spectra File'
,
self
.
datasetParent
.
path
,
'text file (*.txt)'
)[
0
]
if
fname
:
#TODO: implement a more elegant way of testing through the individual imports...
try
:
spectra
,
spectraNames
=
importSpectra
.
importWITecSpectra
(
fname
)
except
ImportError
:
if
specImportEnabled
:
specPath
=
self
.
datasetParent
.
getSpectraFileName
()
if
os
.
path
.
exists
(
specPath
):
spectra
=
np
.
load
(
specPath
)
else
:
fname
=
QtWidgets
.
QFileDialog
.
getOpenFileName
(
QtWidgets
.
QWidget
(),
'Select Spectra File'
,
self
.
datasetParent
.
path
,
'text file (*.txt)'
)[
0
]
if
fname
:
try
:
spectra
,
spectraNames
=
importSpectra
.
importRenishawSpectra
(
fname
)
except
ImportError
:
try
:
spectra
,
spectraNames
=
importSpectra
.
importPerkinElmerSpectra
(
fname
)
except
ImportError
:
pass
if
spectra
is
not
None
:
np
.
save
(
self
.
datasetParent
.
getSpectraFileName
(),
spectra
)
instruments
=
importSpectra
.
listInstruments
()
instrument
,
okPressed
=
QtWidgets
.
QInputDialog
.
getItem
(
QtWidgets
.
QWidget
(),
"File format"
,
"Select instrument:"
,
instruments
,
0
,
False
)
if
okPressed
and
instrument
:
spectra
,
spectraNames
=
importSpectra
.
chooseInstrument
(
instrument
,
fname
)
except
(
ValueError
,
ImportError
):
QtWidgets
.
QMessageBox
.
warning
(
QtWidgets
.
QWidget
(),
'Error'
,
'Unknown format, no spectra loaded.'
)
if
spectra
is
not
None
:
np
.
save
(
self
.
datasetParent
.
getSpectraFileName
(),
spectra
)
return
spectra
def
initializeParticles
(
self
,
numParticles
):
...
...
@@ -104,7 +122,8 @@ class ParticleContainer(object):
for
index
,
particle
in
enumerate
(
self
.
particles
):
particle
.
__dict__
.
update
(
particlestats
[
index
].
__dict__
)
def
testForInconsistentParticleAssignments
(
self
):
#i.e., particles that have multiple measurements with different assignments
def
testForInconsistentParticleAssignments
(
self
):
# Find particles that have multiple measurements with different assignments
self
.
inconsistentParticles
=
[]
for
particle
in
self
.
particles
:
if
not
particle
.
measurementsHaveSameOrigAssignment
():
...
...
@@ -162,7 +181,19 @@ class ParticleContainer(object):
raise
return
particle
def
getParticleOfUID
(
self
,
uid
):
try
:
for
particle
in
self
.
particles
:
if
uid
==
particle
.
uid
:
break
except
:
print
(
'failed getting particle'
)
print
(
'requested Index:'
,
uid
)
print
(
'len particles'
,
len
(
self
.
particles
))
assert
particle
.
uid
==
uid
,
f
'particle.index (
{
particle
.
uid
}
) does match requested index in particleList (
{
uid
}
)'
return
particle
def
getParticleIndexContainingSpecIndex
(
self
,
index
):
for
particle
in
self
.
particles
:
if
index
in
particle
.
getMeasurementIndices
():
...
...
@@ -195,6 +226,10 @@ class ParticleContainer(object):
def
getParticleAssignmentByIndex
(
self
,
partIndex
):
particle
=
self
.
getParticleOfIndex
(
partIndex
)
return
particle
.
getParticleAssignment
()
def
getParticleAssignmentByUID
(
self
,
uid
):
particle
=
self
.
getParticleOfUID
(
uid
)
return
particle
.
getParticleAssignment
()
def
getMeasurementPixelCoords
(
self
)
->
list
:
coords
:
list
=
[]
...
...
@@ -209,7 +244,6 @@ class ParticleContainer(object):
coords
.
append
([
particle
.
aperture
.
centerX
,
particle
.
aperture
.
centerY
])
return
coords
def
getNumberOfParticlesOfAssignment
(
self
,
assignment
):
num
=
0
for
particle
in
self
.
particles
:
...
...
@@ -224,6 +258,10 @@ class ParticleContainer(object):
def
getSpectraIndicesOfParticle
(
self
,
particleIndex
):
particle
=
self
.
getParticleOfIndex
(
particleIndex
)
return
particle
.
getMeasurementIndices
()
def
getSpectraIndicesOfParticleByUID
(
self
,
particleUID
):
particle
=
self
.
getParticleOfUID
(
particleUID
)
return
particle
.
getMeasurementIndices
()
def
getListOfParticleAssignments
(
self
):
particleAssignments
=
[]
...
...
@@ -279,7 +317,15 @@ class ParticleContainer(object):
def
getParticleShapeByIndex
(
self
,
particleIndex
):
particle
=
self
.
getParticleOfIndex
(
particleIndex
)
return
particle
.
shape
def
getParticleIndexByUID
(
self
,
uid
):
for
idx
,
particle
in
enumerate
(
self
.
particles
):
if
uid
==
particle
.
uid
:
break
assert
uid
==
particle
.
uid
return
idx
def
getSizesOfParticleType
(
self
,
assignment
):
particleSizes
=
[]
for
particle
in
self
.
particles
:
...
...
@@ -293,7 +339,14 @@ class ParticleContainer(object):
if
particle
.
getParticleAssignment
()
==
assignment
:
indices
.
append
(
particle
.
index
)
return
indices
def
getUIDsOfParticleType
(
self
,
assignment
):
ids
=
[]
for
particle
in
self
.
particles
:
if
particle
.
getParticleAssignment
()
==
assignment
:
ids
.
append
(
particle
.
uid
)
return
ids
def
getSizeOfParticleByIndex
(
self
,
index
):
particle
=
self
.
getParticleOfIndex
(
index
)
return
particle
.
getParticleSize
()
...
...
@@ -322,16 +375,22 @@ class ParticleContainer(object):
particle
=
self
.
getParticleOfIndex
(
index
)
particle
.
color
=
newColor
particle
.
wasManuallyEdited
=
True
def
changeParticleShape
(
self
,
index
,
newShape
):
particle
=
self
.
getParticleOfIndex
(
index
)
if
'fibre'
==
newShape
:
particle
.
longSize
,
particle
.
shortSize
=
pc
.
getFibreDimension
(
particle
.
contour
)
particle
.
longSize
*=
self
.
datasetParent
.
getPixelScale
()
particle
.
shortSize
*=
self
.
datasetParent
.
getPixelScale
()
particle
.
shape
=
newShape
particle
.
wasManuallyEdited
=
True
def
addMergedParticle
(
self
,
particleIndices
,
newContour
,
newStats
,
newAssignment
=
None
):
def
addMergedParticle
(
self
,
particleIndices
,
newContour
,
newStats
,
newAssignment
=
None
)
->
Particle
:
newParticle
=
Particle
()
newParticle
.
contour
=
newContour
#copy Measurements
#
copy Measurements
for
index
in
particleIndices
:
particle
=
self
.
getParticleOfIndex
(
index
)
for
meas
in
particle
.
getMeasurements
():
...
...
@@ -343,7 +402,12 @@ class ParticleContainer(object):
newParticle
.
__dict__
.
update
(
newStats
.
__dict__
)
newParticle
.
wasManuallyEdited
=
True
self
.
particles
.
append
(
newParticle
)
print
(
'added new particle'
)
return
newParticle
def
removeParticles
(
self
,
indices
):
assert
isinstance
(
indices
,
list
)
for
idx
in
indices
:
self
.
removeParticle
(
idx
)
def
removeParticle
(
self
,
index
):
particle
=
self
.
getParticleOfIndex
(
index
)
#just for asserting to have the correct particle!
...
...
@@ -351,4 +415,4 @@ class ParticleContainer(object):
def
resetParticleIndices
(
self
):
for
newIndex
,
particle
in
enumerate
(
self
.
particles
):
particle
.
index
=
newIndex
\ No newline at end of file
particle
.
index
=
newIndex
analysis/particleEditor.py
View file @
672f17f6
...
...
@@ -25,7 +25,6 @@ from PyQt5 import QtWidgets, QtCore
from
.particlePainter
import
ParticlePainter
from
.
import
particleCharacterization
as
pc
from
..errors
import
NotConnectedContoursError
from
..helperfunctions
import
hasFTIRControl
class
ParticleContextMenu
(
QtWidgets
.
QMenu
):
...
...
@@ -35,21 +34,32 @@ class ParticleContextMenu(QtWidgets.QMenu):
changeParticleColorSignal
=
QtCore
.
pyqtSignal
(
list
,
str
)
changeParticleShapeSignal
=
QtCore
.
pyqtSignal
(
list
,
str
)
deleteParticlesSignal
=
QtCore
.
pyqtSignal
(
list
)
def
__init__
(
self
,
viewparent
):
super
(
ParticleContextMenu
,
self
).
__init__
()
self
.
shapeMenu
=
QtWidgets
.
QMenu
(
"Set Particle Shape To"
)
self
.
colorMenu
=
QtWidgets
.
QMenu
(
"Set Particle Color To"
)
self
.
reassignMenu
=
QtWidgets
.
QMenu
(
"Reassign particle(s) into"
)
self
.
paintMenu
=
QtWidgets
.
QMenu
(
"Paint Mode, merge into"
)
self
.
combineMenu
=
QtWidgets
.
QMenu
(
"Combine Particles into"
)
self
.
deleteAct
=
self
.
addAction
(
"Delete particle(s)"
)
self
.
shapeActs
=
[]
self
.
colorActs
=
[]
self
.
reassignActs
=
[]
self
.
paintActs
=
[]
self
.
combineActs
=
[]
self
.
viewparent
=
viewparent
self
.
selectedParticleIndices
=
self
.
viewparent
.
selectedParticleIndices
self
.
particleContainer
=
self
.
viewparent
.
dataset
.
particleContainer
def
executeAtScreenPos
(
self
,
screenPos
):
self
.
selectedParticleIndices
=
[]
self
.
particleContainer
=
viewparent
.
dataset
.
particleContainer
self
.
createActsAndMenus
()
def
executeAtScreenPos
(
self
,
screenPos
):
action
=
self
.
exec_
(
screenPos
)
if
action
:
actionText
=
self
.
validifyText
(
action
.
text
())
actionText
=
self
.
_valifyText
(
action
.
text
())
if
action
in
self
.
combineActs
:
self
.
combineParticlesSignal
.
emit
(
self
.
selectedParticleIndices
,
actionText
)
elif
action
in
self
.
reassignActs
:
...
...
@@ -62,71 +72,57 @@ class ParticleContextMenu(QtWidgets.QMenu):
self
.
changeParticleShapeSignal
.
emit
(
self
.
selectedParticleIndices
,
actionText
)
elif
action
==
self
.
deleteAct
:
self
.
deleteParticlesSignal
.
emit
(
self
.
selectedParticleIndices
)
def
createActsAndMenus
(
self
):
self
.
combineActs
=
[]
self
.
combineMenu
=
QtWidgets
.
QMenu
(
"Combine Particles into"
)
self
.
paintActs
=
[]
self
.
paintMenu
=
QtWidgets
.
QMenu
(
"Paint Mode, merge into"
)
selctedAssignments
=
[]
def
createActsAndMenus
(
self
):
selected_assignments
=
[]
self
.
selectedParticleIndices
=
self
.
viewparent
.
selectedParticleIndices
for
particleIndex
in
self
.
selectedParticleIndices
:
try
:
assignment
=
self
.
particleContainer
.
getParticleAssignmentByIndex
(
particleIndex
)
except
:
return
selcted
A
ssignments
.
append
(
assignment
)
for
assignment
in
np
.
unique
(
selcted
A
ssignments
):
sel
e
cted
_a
ssignments
.
append
(
assignment
)
for
assignment
in
np
.
unique
(
sel
e
cted
_a
ssignments
):
self
.
combineActs
.
append
(
self
.
combineMenu
.
addAction
(
assignment
))
self
.
paintActs
.
append
(
self
.
paintMenu
.
addAction
(
assignment
))
self
.
combineActs
.
append
(
self
.
combineMenu
.
addAction
(
"other"
))
self
.
paintActs
.
append
(
self
.
paintMenu
.
addAction
(
"other"
))
self
.
reassignActs
=
[]
self
.
reassignMenu
=
QtWidgets
.
QMenu
(
"Reassign particle(s) into"
)
for
polymType
in
self
.
particleContainer
.
getUniquePolymers
():
self
.
reassignActs
.
append
(
self
.
reassignMenu
.
addAction
(
polymType
))
self
.
reassignActs
.
append
(
self
.
reassignMenu
.
addAction
(
"other"
))
num
P
articles
=
len
(
self
.
selectedParticleIndices
)
if
num
P
articles
==
0
:
num
_p
articles
=
len
(
self
.
selectedParticleIndices
)
if
num
_p
articles
==
0
:
self
.
reassignMenu
.
setDisabled
(
True
)
self
.
combineMenu
.
setDisabled
(
True
)
elif
num
P
articles
==
1
:
elif
num
_p
articles
==
1
:
self
.
combineMenu
.
setDisabled
(
True
)
self
.
colorMenu
=
QtWidgets
.
QMenu
(
"Set Particle Color To"
)
self
.
colorActs
=
[]
for
color
in
[
'white'
,
'black'
,
'blue'
,
'brown'
,
'green'
,
'grey'
,
'non-determinable'
,
'red'
,
'transparent'
,
'yellow'
,
'violet'
]:
for
color
in
[
'white'
,
'black'
,
'blue'
,
'brown'
,
'green'
,
'grey'
,
'non-determinable'
,
'red'
,
'transparent'
,
'yellow'
,
'violet'
]:
self
.
colorActs
.
append
(
self
.
colorMenu
.
addAction
(
color
))
self
.
shapeMenu
=
QtWidgets
.
QMenu
(
"Set Particle Shape To"
)
self
.
shapeActs
=
[]
for
shape
in
[
'fibre'
,
'spherule'
,
'irregular'
,
'flake'
]:
self
.
shapeActs
.
append
(
self
.
shapeMenu
.
addAction
(
shape
))
infoAct
=
self
.
addAction
(
f
'selected
{
num
P
articles
}
particles'
)
infoAct
=
self
.
addAction
(
f
'selected
{
num
_p
articles
}
particles'
)
infoAct
.
setDisabled
(
True
)
self
.
addMenu
(
self
.
combineMenu
)
self
.
addMenu
(
self
.
combineMenu
)
self
.
addMenu
(
self
.
reassignMenu
)
self
.
addMenu
(
self
.
paintMenu
)
self
.
addSeparator
()