Module audian.markerdata

Classes

class ColorIconEngine (color)

QIconEngine() QIconEngine(other: QIconEngine)

Expand source code
class ColorIconEngine(QIconEngine):

    def __init__(self, color):
        super().__init__()
        self.color = colors[color]


    def paint(self, painter, rect, mode=QIcon.Normal, state=QIcon.Off):
        painter.setBrush(QColor('black'))
        painter.setPen(Qt.NoPen)
        painter.drawRect(rect)
        painter.setBrush(QColor(self.color))
        painter.setPen(Qt.NoPen)
        d = rect.width()//5
        painter.drawEllipse(rect.adjusted(d, d, -d, -d))

Ancestors

  • PyQt5.QtGui.QIconEngine
  • sip.wrapper
  • sip.simplewrapper

Methods

def paint(self, painter, rect, mode=0, state=1)

paint(self, painter: Optional[QPainter], rect: QRect, mode: QIcon.Mode, state: QIcon.State)

class ColorDelegate (parent=None)

QStyledItemDelegate(parent: Optional[QObject] = None)

Expand source code
class ColorDelegate(QStyledItemDelegate):

    def __init__(self, parent=None):
        super().__init__(parent)


    def createEditor(self, parent, option, index):
        editor = QComboBox(parent)
        for c in colors:
            editor.addItem(index.model().icons[c], c)
        editor.setEditable(False)
        return editor


    def setEditorData(self, editor, index):
        value = index.model().data(index, Qt.EditRole)
        editor.setCurrentText(value)


    def setModelData(self, editor, model, index):
        value = editor.currentText()
        model.setData(index, value, Qt.EditRole)


    def updateEditorGeometry(self, editor, option, index):
        editor.setGeometry(option.rect)

Ancestors

  • PyQt5.QtWidgets.QStyledItemDelegate
  • PyQt5.QtWidgets.QAbstractItemDelegate
  • PyQt5.QtCore.QObject
  • sip.wrapper
  • sip.simplewrapper

Methods

def createEditor(self, parent, option, index)

createEditor(self, parent: Optional[QWidget], option: QStyleOptionViewItem, index: QModelIndex) -> Optional[QWidget]

def setEditorData(self, editor, index)

setEditorData(self, editor: Optional[QWidget], index: QModelIndex)

def setModelData(self, editor, model, index)

setModelData(self, editor: Optional[QWidget], model: Optional[QAbstractItemModel], index: QModelIndex)

def updateEditorGeometry(self, editor, option, index)

updateEditorGeometry(self, editor: Optional[QWidget], option: QStyleOptionViewItem, index: QModelIndex)

class MarkerLabel (label, key_shortcut, color, action=None)
Expand source code
class MarkerLabel:

    def __init__(self, label, key_shortcut, color, action=None):
        self.label = label
        self.key_shortcut = key_shortcut
        self.color = color
        self.action = action


    def copy(self):
        ml = MarkerLabel(self.label, self.key_shortcut, self.color, self.action)
        return ml

Methods

def copy(self)
class MarkerLabelsModel (labels, acts, parent=None)

QAbstractTableModel(parent: Optional[QObject] = None)

Expand source code
class MarkerLabelsModel(QAbstractTableModel):
    
    def __init__(self, labels, acts, parent=None):
        QAbstractTableModel.__init__(self, parent)
        self.orig_labels = labels
        self.labels = [x.copy() for x in labels]
        self.header = ['label', 'key', 'color']
        self.dialog = None
        self.view = None
        self.key_delegate = None
        self.acts = acts
        self.icons = {}
        for c in colors:
            self.icons[c] = QIcon(ColorIconEngine(c))

        
    def rowCount(self, parent=None):
        return len(self.labels)

    
    def columnCount(self, parent=None):
        return 3


    def headerData(self, index, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.header[index]
        if orientation == Qt.Vertical and role == Qt.DisplayRole:
            return f'{index}'
        return QVariant()

    
    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return QVariant()
        
        # data:
        if role == Qt.DisplayRole or role == Qt.EditRole:
            label = self.labels[index.row()]
            if index.column() == 0:
                return label.label
            elif index.column() == 1:
                return label.key_shortcut
            elif index.column() == 2:
                return label.color
            else:
                return QVariant()

        # icons:
        if role == Qt.DecorationRole:
            label = self.labels[index.row()]
            if index.column() == 2:
                return self.icons[label.color]
                
        # alignment:
        if role == Qt.TextAlignmentRole:
            return Qt.AlignLeft | Qt.AlignVCenter

        return QVariant()

    
    def flags(self, index):
        if not index.isValid():
            return Qt.NoItemFlag
        flags = Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled | Qt.ItemIsEditable
        return flags


    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid():
            return False
        if index.column() == 0:
            self.labels[index.row()].label = value
        elif index.column() == 1:
            self.labels[index.row()].key_shortcut = value
            act = self.find_action(value)
            if not act is None:
                act_text = act.text().replace('&', '')
                QMessageBox.information(self.parent(), "Audian key shortcut",
                                        f'Key shortcut <b>{value}</b> for label <b>{self.labels[index.row()].label}</b> disables <b>{act_text}</b>')
        elif index.column() == 2:
            self.labels[index.row()].color = value
        else:
            return False
        self.dataChanged.emit(index, index)
        return True


    def find_action(self, key_shortcut):
        ks = QKeySequence(key_shortcut)
        for a in dir(self.acts):
            act = getattr(self.acts, a)
            if isinstance(act, QAction) and act.shortcut() == ks:
                return act
        return None

                
    def store(self):
        for k in range(len(self.labels)):
            if k < len(self.orig_labels):
                self.orig_labels[k] = self.labels[k]
            else:
                self.orig_labels.append(self.labels[k])

                
    def set(self, labels):
        self.beginResetModel()
        self.orig_labels = labels
        self.labels = [x.copy() for x in labels]
        self.endResetModel()


    def insertRows(self, row, count, parent=QModelIndex()):
        if row > len(self.labels):
            return False
        self.beginInsertRows(parent, row, row+count-1)
        for k in range(count):
            color = list(colors.keys())[(row+k)%len(colors)]
            self.labels.insert(row+k, MarkerLabel(chr(ord('A')+row+k),
                                                  '', color))
        self.endInsertRows()
        return True

        
    def add_row(self):
        self.insertRows(len(self.labels), 1)


    def removeRows(self, row, count, parent=QModelIndex()):
        if row >= len(self.labels):
            return False
        self.beginRemoveRows(parent, row, row+count-1)
        for k in range(count):
            self.labels.pop(row)
        self.endRemoveRows()
        return True

        
    def remove_rows(self):
        selection = self.view.selectionModel()
        if selection.hasSelection():
            rows = [r.row() for r in selection.selectedRows()]
            for r in reversed(sorted(rows)):
                self.removeRow(r)

    
    def edit(self, parent):
        if not self.dialog is None:
            return
        xwidth = parent.fontMetrics().averageCharWidth()
        self.dialog = QDialog(parent)
        self.dialog.setWindowTitle('Audian label editor')
        vbox = QVBoxLayout()
        vbox.setContentsMargins(10, 10, 10, 10)
        self.dialog.setLayout(vbox)
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        vbox.addLayout(hbox)
        self.view = QTableView()
        self.view.setModel(self)
        self.view.resizeColumnsToContents()
        self.view.setColumnWidth(0, max(8*xwidth,
                                        self.view.columnWidth(0)) +
                                 4*xwidth)
        self.view.setColumnWidth(2, max(12*xwidth,
                                        self.view.columnWidth(2)))
        self.view.horizontalHeader().setStretchLastSection(True)
        self.view.setSelectionBehavior(self.view.SelectRows)
        if has_key_editor:
            self.key_delegate = KeySequenceDelegate(self.dialog)
            self.view.setItemDelegateForColumn(1, self.key_delegate)
        color_delegate = ColorDelegate(self.dialog)
        self.view.setItemDelegateForColumn(2, color_delegate)
        hbox.addWidget(self.view)
        bbox = QVBoxLayout()
        bbox.setContentsMargins(0, 0, 0, 0)
        hbox.addLayout(bbox)
        addb = QPushButton('&Add')
        addb.clicked.connect(self.add_row)
        bbox.addWidget(addb)
        delb = QPushButton('&Remove')
        delb.clicked.connect(self.remove_rows)
        bbox.addWidget(delb)
        buttons = QDialogButtonBox(QDialogButtonBox.Cancel |
                                   QDialogButtonBox.Ok)
        buttons.rejected.connect(self.dialog.reject)
        buttons.accepted.connect(self.dialog.accept)
        vbox.addWidget(buttons)
        width = 50 + delb.sizeHint().width()
        width += self.view.verticalHeader().width()
        for c in range(self.columnCount()):
            width += self.view.columnWidth(c)
        self.dialog.setMaximumWidth(width)
        self.dialog.resize(width, 30*xwidth)
        self.dialog.finished.connect(self.finished)
        self.dialog.show()


    def finished(self, result=0):
        if result == QDialog.Accepted:
            self.store()
        self.dialog = None

Ancestors

  • PyQt5.QtCore.QAbstractTableModel
  • PyQt5.QtCore.QAbstractItemModel
  • PyQt5.QtCore.QObject
  • sip.wrapper
  • sip.simplewrapper

Methods

def rowCount(self, parent=None)

rowCount(self, parent: QModelIndex = QModelIndex()) -> int

def columnCount(self, parent=None)

columnCount(self, parent: QModelIndex = QModelIndex()) -> int

def headerData(self, index, orientation, role=0)

headerData(self, section: int, orientation: Qt.Orientation, role: int = Qt.ItemDataRole.DisplayRole) -> Any

def data(self, index, role=0)

data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole) -> Any

def flags(self, index)

flags(self, index: QModelIndex) -> Qt.ItemFlags

def setData(self, index, value, role=2)

setData(self, index: QModelIndex, value: Any, role: int = Qt.ItemDataRole.EditRole) -> bool

def find_action(self, key_shortcut)
def store(self)
def set(self, labels)
def insertRows(self, row, count, parent=<PyQt5.QtCore.QModelIndex object>)

insertRows(self, row: int, count: int, parent: QModelIndex = QModelIndex()) -> bool

def add_row(self)
def removeRows(self, row, count, parent=<PyQt5.QtCore.QModelIndex object>)

removeRows(self, row: int, count: int, parent: QModelIndex = QModelIndex()) -> bool

def remove_rows(self)
def edit(self, parent)
def finished(self, result=0)
class MarkerData
Expand source code
class MarkerData:

    def __init__(self):
        self.file_path = None
        self.channels = []
        self.times = []
        self.amplitudes = []
        self.frequencies = []
        self.powers = []
        self.delta_times = []
        self.delta_amplitudes = []
        self.delta_frequencies = []
        self.delta_powers = []
        self.labels = []
        self.texts = []
        self.keys = ['channels', 'times', 'amplitudes',
                     'frequencies', 'powers',
                     'delta_times', 'delta_amplitudes',
                     'delta_frequencies', 'delta_powers', 'labels', 'texts']
        self.headers = ['channel', 'time/s', 'amplitude',
                       'frequency/Hz', 'power/dB',
                       'time-diff/s', 'ampl-diff',
                        'freq-diff/Hz', 'power-diff/dB', 'label', 'text']

        
    def clear(self):
        self.channels = []
        self.times = []
        self.amplitudes = []
        self.frequencies = []
        self.powers = []
        self.delta_times = []
        self.delta_amplitudes = []
        self.delta_frequencies = []
        self.delta_powers = []
        self.labels = []
        self.texts = []


    def add_data(self, channel, time, amplitude=None,
                 frequency=None, power=None,
                 delta_time=None, delta_amplitude=None,
                 delta_frequency=None, delta_power=None,
                 label='', text=''):
        self.channels.append(channel)
        self.times.append(time if not time is None else np.nan)
        self.amplitudes.append(amplitude if not amplitude is None else np.nan)
        self.frequencies.append(frequency if not frequency is None else np.nan)
        self.powers.append(power if not power is None else np.nan)
        self.delta_times.append(delta_time if not delta_time is None else np.nan)
        self.delta_amplitudes.append(delta_amplitude if not delta_amplitude is None else np.nan)
        self.delta_frequencies.append(delta_frequency if not delta_frequency is None else np.nan)
        self.delta_powers.append(delta_power if not delta_power is None else np.nan)
        self.labels.append(label)
        self.texts.append(text)

        
    def set_label(self, index, label):
        self.labels[index] = label

        
    def set_text(self, index, text):
        self.texts[index] = text


    def data_frame(self):
        table_dict = {}
        for key, header in zip(self.keys, self.headers):
            table_dict[header] = getattr(self, key)
        return pd.DataFrame(table_dict)


    def set_markers(self, locs, labels, rate):
        for i in range(len(locs)):
            l = ''
            t = ''
            if i < len(labels):
                l = labels[i,0]
                t = labels[i,1]
            tstart = float(locs[i,0])/rate
            tspan = float(locs[i,1])/rate
            self.add_data(0, tstart + tspan, delta_time=tspan,
                          label=l, text=t)

            
    def get_markers(self, rate):
        n = len(self.times)
        locs = np.zeros((n, 2), dtype=int)
        labels = np.zeros((n, 3), dtype=object)
        for k in range(n):
            ispan = int(np.round(self.delta_times[k]*rate))
            i1 = int(np.round(self.times[k]*rate))
            locs[k,0] = i1 - ispan
            locs[k,1] = ispan
            labels[k,0] = self.labels[k]
            labels[k,1] = self.texts[k]
        return locs, labels

Methods

def clear(self)
def add_data(self, channel, time, amplitude=None, frequency=None, power=None, delta_time=None, delta_amplitude=None, delta_frequency=None, delta_power=None, label='', text='')
def set_label(self, index, label)
def set_text(self, index, text)
def data_frame(self)
def set_markers(self, locs, labels, rate)
def get_markers(self, rate)
class MarkerDataModel (data, parent=None)

QAbstractTableModel(parent: Optional[QObject] = None)

Expand source code
class MarkerDataModel(QAbstractTableModel):
    
    def __init__(self, data, parent=None):
        QAbstractTableModel.__init__(self, parent)
        self.data = data

        
    def rowCount(self, parent=None):
        return len(self.data.channels)

    
    def columnCount(self, parent=None):
        return len(self.data.keys)


    def headerData(self, index, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.data.headers[index]
        if orientation == Qt.Vertical and role == Qt.DisplayRole:
            return f'{index}'
        return QVariant()

    
    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return QVariant()
        
        key = self.data.keys[index.column()]
        item = getattr(self.data, key)[index.row()]
        
        # data:
        if role == Qt.DisplayRole or role == Qt.EditRole:
            if key == 'labels' or key == 'texts':
                return item
            else:
                if item is np.nan:
                    return '-'
                else:
                    return f'{item:.5g}'
                
        # alignment:
        if role == Qt.TextAlignmentRole:
            if key == 'labels' or key == 'texts':
                return Qt.AlignLeft | Qt.AlignVCenter
            else:
                if item is np.nan:
                    return Qt.AlignHCenter | Qt.AlignVCenter
                else:
                    return Qt.AlignRight | Qt.AlignVCenter

        return QVariant()

    
    def flags(self, index):
        if not index.isValid():
            return Qt.NoItemFlag
        flags = Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled
        key = self.data.keys[index.column()]
        if key == 'labels':
            return flags | Qt.ItemIsEditable
        else:
            return flags


    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid():
            return False
        key = self.data.keys[index.column()]
        if key == 'labels':
            self.data.labels[index.row()] = value
            self.dataChanged.emit(index, index)
            return True
        else:
            return False
        

    def clear(self):
        self.beginResetModel()
        self.data.clear()
        self.endResetModel()


    def save(self, parent):
        name = os.path.splitext(os.path.basename(self.data.file_path))[0]
        file_name = f'{name}-events.csv'
        filters = 'All files (*);;Comma separated values CSV (*.csv)'
        has_excel = False
        try:
            import openpyxl
            has_excel = True
            filters += ';;Excel spreadsheet XLSX (*.xlsx)'
        except ImportError:
            pass
        file_path = os.path.join(os.path.dirname(self.data.file_path),
                                 file_name)
        file_path = QFileDialog.getSaveFileName(parent, 'Save marker data',
                                                file_path, filters)[0]
        if file_path:
            df = self.data.data_frame()
            ext = os.path.splitext(file_path)[1]
            if has_excel and ext.lower() == '.xlsx':
                df.to_excel(file_path, index=False)
            else:
                df.to_csv(file_path, index=False)
            

    def add_data(self, channel, time, amplitude, frequency, power,
                 delta_time=None, delta_amplitude=None,
                 delta_frequency=None, delta_power=None, label=''):
        self.beginInsertRows(QModelIndex(),
                             len(self.data.channels), len(self.data.channels))
        self.data.add_data(channel, time, amplitude, frequency, power,
                           delta_time, delta_amplitude,
                           delta_frequency, delta_power, label)
        self.endInsertRows()

Ancestors

  • PyQt5.QtCore.QAbstractTableModel
  • PyQt5.QtCore.QAbstractItemModel
  • PyQt5.QtCore.QObject
  • sip.wrapper
  • sip.simplewrapper

Methods

def rowCount(self, parent=None)

rowCount(self, parent: QModelIndex = QModelIndex()) -> int

def columnCount(self, parent=None)

columnCount(self, parent: QModelIndex = QModelIndex()) -> int

def headerData(self, index, orientation, role=0)

headerData(self, section: int, orientation: Qt.Orientation, role: int = Qt.ItemDataRole.DisplayRole) -> Any

def data(self, index, role=0)

data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole) -> Any

def flags(self, index)

flags(self, index: QModelIndex) -> Qt.ItemFlags

def setData(self, index, value, role=2)

setData(self, index: QModelIndex, value: Any, role: int = Qt.ItemDataRole.EditRole) -> bool

def clear(self)
def save(self, parent)
def add_data(self, channel, time, amplitude, frequency, power, delta_time=None, delta_amplitude=None, delta_frequency=None, delta_power=None, label='')