Skip to content

Commit

Permalink
implement __getstate__ and __setstate__ for data matrix; functionaliz…
Browse files Browse the repository at this point in the history
…e toggle buttons
  • Loading branch information
paulmueller committed Jul 4, 2019
1 parent 49941ac commit ddf271a
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 74 deletions.
4 changes: 3 additions & 1 deletion shapeout2/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ def on_quickviewed(self, path, filters):
# update quick view subwindow
self.widget_quick_view.show_rtdc(path, filters)
# show quick view subwindow
self.subwindows["quick_view"].setVisible(True)
if not self.subwindows["quick_view"].isVisible():
self.toolButton_quick_view.toggle()
self.subwindows["quick_view"].setVisible(True)

def on_splitter(self):
if self.splitter.sizes()[0] == 0:
Expand Down
2 changes: 1 addition & 1 deletion shapeout2/gui/main.ui
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>109</width>
<width>159</width>
<height>569</height>
</rect>
</property>
Expand Down
153 changes: 148 additions & 5 deletions shapeout2/gui/matrix.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import copy
import pathlib

from PyQt5 import QtCore, QtWidgets
Expand All @@ -19,24 +20,65 @@ def __init__(self, parent=None, analysis=range(3)):
self.setLayout(self.layout)
self.setAcceptDrops(True)

# used for toggling between all active, all inactive and semi state
self.semi_states_dataset = {}
self.semi_states_filter = {}

def __getstate__(self):
"""Logical states of the current data matrix"""
# datasets
nrows = self.layout.rowCount()
datasets = []
for ii in range(1, nrows):
ds = self.layout.itemAtPosition(ii, 0).widget()
datasets.append(ds.__getstate__())
# filters
ncols = self.layout.columnCount()
filters = []
for jj in range(1, ncols):
f = self.layout.itemAtPosition(0, jj).widget()
filters.append(f.__getstate__())
# states
mestates = {}
for si in range(1, nrows):
idds = self.layout.itemAtPosition(si, 0).widget().identifier
idict = {}
for sj in range(1, ncols):
idf = self.layout.itemAtPosition(0, sj).widget().identifier
me = self.layout.itemAtPosition(si, sj).widget()
idict[idf] = me.__getstate__()
mestates[idds] = idict
state = {"elements": mestates,
"datasets": datasets,
"filters": filters}
return state

def __setstate__(self, state):
raise NotImplementedError("TODO")

def add_dataset(self, path, row=None):
nrows = self.layout.rowCount()
if nrows == 1:
self.add_filter()

if row is None:
self.layout.addWidget(MatrixDataset(path), nrows, 0)
md = MatrixDataset(path)
self.layout.addWidget(md, nrows, 0)
else:
# TODO: insert dataset at row
assert False

md.active_toggled.connect(self.toggle_dataset_active)

self.fill_elements()
self.adjust_size()

def add_filter(self, evt=None):
ncols = self.layout.columnCount()
name = "FS{}".format(ncols)
self.layout.addWidget(MatrixFilter(name), 0, ncols)
f = MatrixFilter(name)
f.active_toggled.connect(self.toggle_filter_active)
self.layout.addWidget(f, 0, ncols)
self.fill_elements()
self.adjust_size()

Expand Down Expand Up @@ -75,19 +117,120 @@ def fill_elements(self):
self.layout.addWidget(me, ii, jj)

def get_dataset_paths(self):
"""Return dataset paths in the order they are displayed"""
"""Return dataset paths in the order they are shown"""
nrows = self.layout.rowCount()
paths = []
for ii in range(1, nrows):
item = self.layout.itemAtPosition(ii, 0)
paths.append(item.widget().path)
return paths

def update_content(self):
def get_matrix_element(self, dataset_id, filter_id):
"""Return matrix element matching dataset and filter identifiers"""
ncols = self.layout.columnCount()
nrows = self.layout.rowCount()
for ii in range(1, nrows):
for jj in range(1, ncols):
ds = self.layout.itemAtPosition(ii, 0).widget()
if ds.identifier == dataset_id:
for jj in range(1, ncols):
f = self.layout.itemAtPosition(0, jj).widget()
if f.identifier == filter_id:
break
break
return self.layout.itemAtPosition(ii, jj).widget()

@QtCore.pyqtSlot()
def toggle_dataset_active(self):
"""Switch between all active, all inactive, previous state
Modifies the matrix elements for a dataset/row,
which is defined by the signal sender :class:`MatrixDataset`.
Cyclic toggling order: semi -> all -> none
"""
self.semi_states_filter = {}
sender = self.sender()
sid = sender.identifier
state = self.__getstate__()["elements"][sid]
num_actives = sum([s["active"] for s in state.values()])

# update state according to the scheme in the docstring
if num_actives == 0:
if sid in self.semi_states_dataset:
# use semi state
oldstate = self.semi_states_dataset[sid]
for key in oldstate:
if key in state:
state[key] = oldstate[key]
else:
# toggle all to active
for key in state:
state[key]["active"] = True
elif num_actives == len(state):
# toggle all to inactive
for key in state:
state[key]["active"] = False
else:
# save semi state
self.semi_states_dataset[sid] = copy.deepcopy(state)
# toggle all to active
for key in state:
state[key]["active"] = True

for fid in state:
me = self.get_matrix_element(sid, fid)
me.__setstate__(state[fid])

@QtCore.pyqtSlot()
def toggle_filter_active(self):
"""Switch between all active, all inactive, previous state
Modifies the matrix elements for a filter/column,
which is defined by the signal sender :class:`MatrixFilter`.
Cyclic toggling order: semi -> all -> none
"""
self.semi_states_dataset = {}
sender = self.sender()
sid = sender.identifier

states = self.__getstate__()["elements"]
state = {}
for key in states:
state[key] = states[key][sid]

num_actives = sum([s["active"] for s in state.values()])

# update state according to the scheme in the docstring
if num_actives == 0:
if sid in self.semi_states_filter:
# use semi state
oldstate = self.semi_states_filter[sid]
for key in oldstate:
if key in state:
state[key] = oldstate[key]
else:
# toggle all to active
for key in state:
state[key]["active"] = True
elif num_actives == len(state):
# toggle all to inactive
for key in state:
state[key]["active"] = False
else:
# save semi state
self.semi_states_filter[sid] = copy.deepcopy(state)
# toggle all to active
for key in state:
state[key]["active"] = True

for dsid in state:
me = self.get_matrix_element(dsid, sid)
me.__setstate__(state[dsid])

def update_content(self):
ncols = self.layout.columnCount()
nrows = self.layout.rowCount()
for ii in range(nrows):
for jj in range(ncols):
item = self.layout.itemAtPosition(ii, jj)
if isinstance(item, MatrixElement):
item.update_content()
Expand Down
64 changes: 57 additions & 7 deletions shapeout2/gui/matrix_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,71 @@


class MatrixDataset(QtWidgets.QWidget):
def __init__(self, path):
_instance_counter = 0
active_toggled = QtCore.pyqtSignal()
enabled_toggled = QtCore.pyqtSignal()

def __init__(self, path=None):
"""Create a new dataset matrix element
If `path` is None, a dummy element is inserted which needs
to be updated with :func:`MatrixDataset.__setstate__`.
"""
QtWidgets.QWidget.__init__(self)
path_ui = pkg_resources.resource_filename(
"shapeout2.gui", "matrix_dataset.ui")
uic.loadUi(path_ui, self)

title = meta_tool.get_repr(path, append_path=True)
MatrixDataset._instance_counter += 1
self.identifier = "ds{}".format(MatrixDataset._instance_counter)
self.path = path

# options button
menu = QtWidgets.QMenu()
menu.addAction('insert anew', self.action_insert_anew)
menu.addAction('duplicate', self.action_duplicate)
menu.addAction('remove', self.action_remove)
self.pushButton_opt.setMenu(menu)

# toggle all active, all inactive, semi state
self.pushButton_toggle.clicked.connect(self.active_toggled.emit)

# toggle enabled/disabled state
self.checkBox.clicked.connect(self.enabled_toggled.emit)

# set tooltip/label
self.update_content()

self.setFixedSize(QtCore.QSize(80, 80))
self.resize(QtCore.QSize(80, 80))
self.setMaximumSize(QtCore.QSize(80, 80))

self.setToolTip(title)
self.label.setToolTip(title)
if len(title) > 8:
title = title[:5] + "..."
self.label.setText(title)
def __getstate__(self):
state = {"path": self.path,
"identifier": self.identifier,
}
return state

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

def action_duplicate(self):
pass

def action_insert_anew(self):
pass

def action_remove(self):
pass

def update_content(self):
"""Reset tool tips and title"""
if self.path is not None:
title = meta_tool.get_repr(self.path, append_path=True)
self.setToolTip(title)
self.label.setToolTip(title)
if len(title) > 8:
title = title[:5] + "..."
self.label.setText(title)
29 changes: 7 additions & 22 deletions shapeout2/gui/matrix_dataset.ui
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<widget class="QPushButton" name="pushButton_toggle">
<property name="text">
<string>toggle</string>
</property>
Expand Down Expand Up @@ -155,44 +155,29 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QComboBox" name="comboBox">
<widget class="QPushButton" name="pushButton_opt">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="frame">
<bool>false</bool>
<property name="text">
<string>opt</string>
</property>
<item>
<property name="text">
<string>insert anew</string>
</property>
</item>
<item>
<property name="text">
<string>duplicate</string>
</property>
</item>
<item>
<property name="text">
<string>remove</string>
</property>
</item>
</widget>
</item>
</layout>
Expand Down

0 comments on commit ddf271a

Please sign in to comment.