Skip to content

Commit

Permalink
implement basic Quick View functionalities
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmueller committed Jul 9, 2019
1 parent 6b51a60 commit 2207a48
Show file tree
Hide file tree
Showing 7 changed files with 468 additions and 9 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
2.0.0a17
- implement data/plot matrix states
- implement data/plot matrix logic
2.0.0a16
- implement basic Quick View functionalities
2.0.0a16
- add documentation (from Shape-Out 1)
- test deployment for macOS
2.0.0a1
Expand Down
59 changes: 59 additions & 0 deletions shapeout2/filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Keep record of all filters used"""
import warnings


class Filter(object):
_instance_counter = 0
_instances = {}

def __init__(self, identifier=None):
if identifier is None:
identifier = "Filter_{}".format(Filter._instance_counter)
self.identifier = identifier
Filter._instances[identifier] = self

@property
def hash(self):
"""Return the hash of the filter"""
warnings.warn("Filter hashing not implemented yet!")
return self.identifier

@staticmethod
def get_filter(identifier):
"""Get the filter with the given identifier.
Notes
-----
Creates the filter if it does not exist.
"""
if identifier in Filter._instances:
f = Filter._instances[identifier]
else:
f = Filter(identifier=identifier)
return f

def apply_to(self, dataset):
"""Apply a filter to an instance of RTDCBase
Parameters
----------
dataset: RTDCBase
Input dataset
Returns
-------
filtered_dataset: RTDCBase
Either the input dataset, or a hierarchy child.
Notes
-----
The filter should be applied like so:
`ds = filter_instance.apply_to(ds)`
This is necessary to make sure that hierarchy leveling
filters are applied correctly, i.e. a hierarchy child
is returned in this case and you want to work with that
instead of the original dataset.
"""
warnings.warn("applying filters not yet implemented!")
return dataset
123 changes: 118 additions & 5 deletions shapeout2/gui/quick_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from PyQt5 import uic, QtCore, QtWidgets
import pyqtgraph as pg

from .. import meta_tool
from .. import plot_cache


class QuickView(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
Expand All @@ -13,9 +16,83 @@ def __init__(self, *args, **kwargs):
"shapeout2.gui", "quick_view.ui")
uic.loadUi(path_ui, self)
self.setWindowTitle("Quick View (QV)")

# Scatter plot
self.scatter_plot = self.widget_scatter.plot
self.scatter_plot.sigClicked.connect(self.clicked)

# Set scale options (with data)
for cb in [self.comboBox_xscale, self.comboBox_yscale]:
cb.clear()
cb.addItem("linear", "linear")
cb.addItem("logarithmic", "log")

# Hide settings by default
self.widget_settings.setVisible(False)

# initial value
self.path = None
self.filters = []

# value changed signals
self.signal_widgets = [self.checkBox_downsample,
self.spinBox_downsample,
self.comboBox_x,
self.comboBox_y,
self.comboBox_xscale,
self.comboBox_yscale,
self.checkBox_isoelastics,
]
for w in self.signal_widgets:
if hasattr(w, "currentIndexChanged"):
w.currentIndexChanged.connect(self.plot)
elif hasattr(w, "stateChanged"):
w.stateChanged.connect(self.plot)

def __getstate__(self):
state = {"path": self.path,
"downsampling enabled": self.checkBox_downsample.isChecked(),
"downsampling value": self.spinBox_downsample.value(),
"axis x": self.comboBox_x.currentData(),
"axis y": self.comboBox_y.currentData(),
"scale x": self.comboBox_xscale.currentData(),
"scale y": self.comboBox_yscale.currentData(),
"isoelastics enabled": self.checkBox_isoelastics.isChecked(),
"filters": self.filters,
}
return state

def __setstate__(self, state):
for tb in self.signal_widgets:
tb.blockSignals(True)
self.path = state["path"]
# downsampling
self.checkBox_downsample.setChecked(state["downsampling enabled"])
self.spinBox_downsample.setValue(state["downsampling value"])
# axes combobox choices
ds_features = meta_tool.get_rtdc_features(state["path"])
for cb in [self.comboBox_x, self.comboBox_y]:
# set features
cb.clear()
for feat in dclab.dfn.scalar_feature_names:
if feat in ds_features:
cb.addItem(dclab.dfn.feature_name2label[feat], feat)
# axes labels
idx = self.comboBox_x.findData(state["axis x"])
self.comboBox_x.setCurrentIndex(idx)
idy = self.comboBox_y.findData(state["axis y"])
self.comboBox_y.setCurrentIndex(idy)
# scaling
idxs = self.comboBox_xscale.findData(state["scale x"])
self.comboBox_xscale.setCurrentIndex(idxs)
idys = self.comboBox_yscale.findData(state["scale y"])
self.comboBox_yscale.setCurrentIndex(idys)
# isoelastics
self.checkBox_isoelastics.setChecked(state["isoelastics enabled"])
self.filters = state["filters"]
for tb in self.signal_widgets:
tb.blockSignals(False)

def clicked(self, plot, points):
for p in plot.lastClicked:
p.resetPen()
Expand All @@ -24,13 +101,49 @@ def clicked(self, plot, points):
p.setPen('b', width=2)
plot.lastClicked = points

def plot(self):
state = self.__getstate__()
downsample = state["downsampling enabled"] * \
state["downsampling value"]
x, y = plot_cache.get_downsampled_scatter(
path=state["path"],
filters=state["filters"],
downsample=downsample,
xax=state["axis x"],
yax=state["axis y"],
xscale=state["scale x"],
yscale=state["scale y"])
self.scatter_plot.clear()
self.scatter_plot.setData(x=x, y=y)
# TODO: draw isoelasticity lines

@QtCore.pyqtSlot(pathlib.Path, list)
def show_rtdc(self, path, filters):
axis_x = "area_um"
axis_y = "deform"
ds = dclab.new_dataset(path)
self.scatter_plot.clear()
self.scatter_plot.setData(x=ds[axis_x], y=ds[axis_y])
state = self.__getstate__()
state["path"] = path
state["filters"] = filters
# default features (plot axes)
if state["axis x"] is None:
state["axis x"] = "area_um"
if state["axis y"] is None:
state["axis y"] = "deform"
# check whether axes exist in ds and change them if necessary
ds_features = meta_tool.get_rtdc_features(path)
if state["axis x"] not in ds_features:
for feat in dclab.dfn.scalar_feature_names:
if feat in ds_features:
state["axis x"] = feat
break
if state["axis y"] not in ds_features:
for feat in dclab.dfn.scalar_feature_names:
if feat in ds_features:
state["axis y"] = feat
if feat != state["axis y"]:
# If there is only one feature, at least we
# have set the state to a reasonable value.
break
self.__setstate__(state)
self.plot()


class RTDCScatterWidget(pg.PlotWidget):
Expand Down

0 comments on commit 2207a48

Please sign in to comment.