Skip to content

Commit

Permalink
implement data matrix logic
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmueller committed Jul 8, 2019
1 parent e2f5130 commit 987a921
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
2.0.0a17
- implement data matrix states
- implement data matrix logic
2.0.0a16
- add documentation (from Shape-Out 1)
- test deployment for macOS
Expand Down
104 changes: 97 additions & 7 deletions shapeout2/gui/matrix.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import copy
import pathlib

import numpy as np
from PyQt5 import QtCore, QtWidgets

from .matrix_dataset import MatrixDataset
Expand Down Expand Up @@ -71,6 +72,7 @@ def __setstate__(self, state):
el_state = ds_state[f_key]
el = self.get_matrix_element(ds_key, f_key)
el.__setstate__(el_state)
self.adjust_size()

def _reset_layout(self):
if self.glo is not None:
Expand Down Expand Up @@ -108,28 +110,31 @@ def add_dataset(self, path):
md = MatrixDataset(path)
self.glo.addWidget(md, self.num_datasets+1, 0)
md.active_toggled.connect(self.toggle_dataset_active)
md.enabled_toggled.connect(self.toggle_dataset_enable)
md.option_action.connect(self.on_option_dataset)
self.fill_elements()
self.adjust_size()
return md

def add_filter(self, evt=None):
name = "FS{}".format(self.num_filters+1)
f = MatrixFilter(name)
f.active_toggled.connect(self.toggle_filter_active)
self.glo.addWidget(f, 0, self.num_filters+1)
mf = MatrixFilter(name)
mf.active_toggled.connect(self.toggle_filter_active)
mf.enabled_toggled.connect(self.toggle_filter_enable)
mf.option_action.connect(self.on_option_filter)
self.glo.addWidget(mf, 0, self.num_filters+1)
self.fill_elements()
self.adjust_size()
return f
return mf

def adjust_size(self):
self.update()
QtWidgets.QApplication.processEvents()
ncols = self.num_filters
nrows = self.num_datasets
if ncols > 1 and nrows > 1:
hwidth = self.glo.itemAtPosition(0, 1).geometry().width() + 2
hheight = self.glo.itemAtPosition(0, 1).geometry().height() + 2
dwidth = self.glo.itemAtPosition(1, 0).geometry().width() + 2
hheight = self.glo.itemAtPosition(0, 1).geometry().height()
dwidth = self.glo.itemAtPosition(1, 0).geometry().width()
dheight = self.glo.itemAtPosition(1, 0).geometry().height() + 2
self.setMinimumSize((ncols)*hwidth+dwidth,
(nrows)*dheight+hheight)
Expand All @@ -151,12 +156,32 @@ def dropEvent(self, event):
event.ignore()

def fill_elements(self):
# add widgets
for ii in range(self.num_datasets):
for jj in range(self.num_filters):
if self.glo.itemAtPosition(ii+1, jj+1) is None:
me = MatrixElement()
me.quickview_selected.connect(self.update_quickview)
self.glo.addWidget(me, ii+1, jj+1)
# make sure enabled/disabled is honored
state = self.__getstate__()
for ds in state["datasets"]:
for f in state["filters"]:
if not ds["enabled"] or not f["enabled"]:
me = self.get_matrix_element(ds["identifier"], f["identifier"])
mstate = me.__getstate__()
mstate["enabled"] = False
me.__setstate__(mstate)

def get_dataset(self, dataset_id):
nrows = self.glo.rowCount()
for ii in range(1, nrows):
ds = self.glo.itemAtPosition(ii, 0).widget()
if ds.identifier == dataset_id:
break
else:
raise KeyError("Dataset '{}' not found!".format(dataset_id))
return ds

def get_dataset_paths(self):
"""Return dataset paths in the order they are shown"""
Expand All @@ -167,6 +192,16 @@ def get_dataset_paths(self):
paths.append(item.widget().path)
return paths

def get_filter(self, filter_id):
ncols = self.glo.columnCount()
for jj in range(1, ncols):
f = self.glo.itemAtPosition(0, jj).widget()
if f.identifier == filter_id:
break
else:
raise KeyError("Filter '{}' not found!".format(filter_id))
return f

def get_matrix_element(self, dataset_id, filter_id):
"""Return matrix element matching dataset and filter identifiers"""
ncols = self.glo.columnCount()
Expand All @@ -178,7 +213,11 @@ def get_matrix_element(self, dataset_id, filter_id):
f = self.glo.itemAtPosition(0, jj).widget()
if f.identifier == filter_id:
break
else:
raise KeyError("Filter '{}' not found!".format(filter_id))
break
else:
raise KeyError("Dataset '{}' not found!".format(dataset_id))
return self.glo.itemAtPosition(ii, jj).widget()

@QtCore.pyqtSlot(str)
Expand All @@ -192,6 +231,8 @@ def on_option_dataset(self, option):
if option == "insert_anew":
ds_new = self.add_dataset(path=None)
ds_state["identifier"] = ds_new.identifier
# enable by default
ds_state["enabled"] = True
state["datasets"].insert(row, ds_state)
elif option == "duplicate":
ds_new = self.add_dataset(path=None)
Expand All @@ -205,6 +246,25 @@ def on_option_dataset(self, option):
state["elements"].pop(ds_state["identifier"])
self.__setstate__(state)

@QtCore.pyqtSlot(str)
def on_option_filter(self, option):
"""Filter option logic (remove, insert_anew, duplicate)"""
sender = self.sender()
idx = self.glo.indexOf(sender)
_, column, _, _ = self.glo.getItemPosition(idx)
state = self.__getstate__()
f_state = sender.__getstate__()
if option == "duplicate":
f_new = self.add_filter()
f_state["identifier"] = f_new.identifier
f_state["title"] += "({})".format(f_new.identifier)
state["filters"].insert(column, f_state)
else: # remove
state["filters"].pop(column-1)
for ds_key in state["elements"]:
state["elements"][ds_key].pop(f_state["identifier"])
self.__setstate__(state)

@QtCore.pyqtSlot()
def toggle_dataset_active(self):
"""Switch between all active, all inactive, previous state
Expand Down Expand Up @@ -246,6 +306,21 @@ def toggle_dataset_active(self):
me = self.get_matrix_element(sid, fid)
me.__setstate__(state[fid])

@QtCore.pyqtSlot(bool)
def toggle_dataset_enable(self, enabled):
sender = self.sender()
sid = sender.identifier
state = self.__getstate__()
for f_key in state["elements"][sid]:
# make sure that disabled filters are honored
fstate = self.get_filter(f_key).__getstate__()
fenabled = fstate["enabled"]
# update element widget
me = self.get_matrix_element(sid, f_key)
mstate = me.__getstate__()
mstate["enabled"] = np.logical_and(enabled, fenabled)
me.__setstate__(mstate)

@QtCore.pyqtSlot()
def toggle_filter_active(self):
"""Switch between all active, all inactive, previous state
Expand Down Expand Up @@ -292,6 +367,21 @@ def toggle_filter_active(self):
me = self.get_matrix_element(dsid, sid)
me.__setstate__(state[dsid])

@QtCore.pyqtSlot(bool)
def toggle_filter_enable(self, enabled):
sender = self.sender()
sid = sender.identifier
state = self.__getstate__()
for ds_key in state["elements"]:
# make sure that disabled filters are honored
dstate = self.get_dataset(ds_key).__getstate__()
denabled = dstate["enabled"]
# update element widget
me = self.get_matrix_element(ds_key, sid)
mstate = me.__getstate__()
mstate["enabled"] = np.logical_and(enabled, denabled)
me.__setstate__(mstate)

def update_content(self):
ncols = self.glo.columnCount()
nrows = self.glo.rowCount()
Expand Down
4 changes: 3 additions & 1 deletion shapeout2/gui/matrix_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
class MatrixDataset(QtWidgets.QWidget):
_instance_counter = 0
active_toggled = QtCore.pyqtSignal()
enabled_toggled = QtCore.pyqtSignal()
enabled_toggled = QtCore.pyqtSignal(bool)
option_action = QtCore.pyqtSignal(str)

def __init__(self, path=None):
Expand Down Expand Up @@ -45,12 +45,14 @@ def __init__(self, path=None):
def __getstate__(self):
state = {"path": self.path,
"identifier": self.identifier,
"enabled": self.checkBox.isChecked(),
}
return state

def __setstate__(self, state):
self.identifier = state["identifier"]
self.path = state["path"]
self.checkBox.setChecked(state["enabled"])
self.update_content()

def action_duplicate(self):
Expand Down
8 changes: 7 additions & 1 deletion shapeout2/gui/matrix_dataset.ui
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,18 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<property name="minimumSize">
<size>
<width>80</width>
<height>80</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>120</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
Expand Down
6 changes: 3 additions & 3 deletions shapeout2/gui/matrix_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ def update_content(self, quickview=False):
label = "active"
tooltip = "Click to deactivate"
elif self.active and not self.enabled:
color = "#A4D5A7" # gray-green
color = "#C9DAC9" # gray-green
label = "active\n(disabled)"
tooltip = "Click to deactivate"
elif not self.active and self.enabled:
color = "#EFEFEF" # light gray
label = "inactive"
tooltip = "Click to activate"
else:
color = "#C0C1C0" # gray
label = "inactive"
color = "#DCDCDC" # gray
label = "inactive\n(disabled)"
tooltip = "Click to activate"

curinst = MatrixElement._quick_view_instance
Expand Down
9 changes: 6 additions & 3 deletions shapeout2/gui/matrix_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
class MatrixFilter(QtWidgets.QWidget):
_instance_counter = 0
active_toggled = QtCore.pyqtSignal()
enabled_toggled = QtCore.pyqtSignal()
enabled_toggled = QtCore.pyqtSignal(bool)
option_action = QtCore.pyqtSignal(str)

def __init__(self, title="FS?"):
QtWidgets.QWidget.__init__(self)
Expand Down Expand Up @@ -36,19 +37,21 @@ def __init__(self, title="FS?"):
def __getstate__(self):
state = {"title": self.title,
"identifier": self.identifier,
"enabled": self.checkBox.isChecked(),
}
return state

def __setstate__(self, state):
self.identifier = state["identifier"]
self.title = state["title"]
self.checkBox.setChecked(state["enabled"])
self.update_content()

def action_duplicate(self):
pass
self.option_action.emit("duplicate")

def action_remove(self):
pass
self.option_action.emit("remove")

def update_content(self):
"""Reset tool tips and title"""
Expand Down

0 comments on commit 987a921

Please sign in to comment.