from typing import TYPE_CHECKING, List, Callable from PyQt5 import QtWidgets as qw, QtCore as qc from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeySequence from PyQt5.QtWidgets import QShortcut from corrscope.gui.util import find_ranges qsp = qw.QSizePolicy if TYPE_CHECKING: from corrscope.gui import ChannelModel class ChannelTableView(qw.QTableView): def append_channels(self, wavs: List[str]): model: "ChannelModel" = self.model() begin_row = model.rowCount() count_rows = len(wavs) col = model.idx_of_key["wav_path"] # Insert N empty rows into model (mutates cfg.channels). model.insertRows(begin_row, count_rows) # Fill N empty rows with wav_path. for row, wav_path in enumerate(wavs, begin_row): index = model.index(row, col) model.setData(index, wav_path) def delete_selected(self): model: "ChannelModel" = self.model() rows = self.selected_rows() row_ranges = find_ranges(rows) for first_row, nrow in reversed(list(row_ranges)): model.removeRows(first_row, nrow) def on_channel_up(self): self.move_selection(-1) def on_channel_down(self): self.move_selection(1) def move_selection(self, delta: int): model: "ChannelModel" = self.model() rows = self.selected_rows() row_ranges = find_ranges(rows) # If we hit the end, cancel all other moves. # If moving up, move top first. if delta > 0: # If moving down, move bottom first. row_ranges = reversed(list(row_ranges)) parent = qc.QModelIndex() for first_row, nrow in row_ranges: if delta > 0: dest_row = first_row + nrow + delta else: dest_row = first_row + delta if not model.moveRows(parent, first_row, nrow, parent, dest_row): break def selected_rows(self) -> List[int]: sel: qc.QItemSelectionModel = self.selectionModel() inds: List[qc.QModelIndex] = sel.selectedIndexes() rows: List[int] = sorted({ind.row() for ind in inds}) return rows class ShortcutButton(qw.QPushButton): scoped_shortcut: QShortcut def add_shortcut(self, scope: qw.QWidget, shortcut: str) -> None: """ Adds shortcut and tooltip. """ self.scoped_shortcut = new_shortcut(shortcut, scope, self.click) parsed_keys: QKeySequence = self.scoped_shortcut.key() self.setToolTip(parsed_keys.toString(QKeySequence.NativeText)) def new_shortcut(shortcut: str, scope: qw.QWidget, slot: Callable) -> qw.QShortcut: parsed_keys = QKeySequence(shortcut, QKeySequence.PortableText) scoped_shortcut = qw.QShortcut(parsed_keys, scope) scoped_shortcut.setContext(Qt.WidgetWithChildrenShortcut) scoped_shortcut.activated.connect(slot) return scoped_shortcut class TabWidget(qw.QTabWidget): def __init__(self, *args, **kwargs): qw.QTabWidget.__init__(self, *args, **kwargs) new_shortcut("ctrl+tab", self, self.next_tab) new_shortcut("ctrl+pgDown", self, self.next_tab) new_shortcut("ctrl+shift+tab", self, self.prev_tab) new_shortcut("ctrl+pgUp", self, self.prev_tab) def next_tab(self): self.setCurrentIndex((self.currentIndex() + 1) % self.count()) def prev_tab(self): self.setCurrentIndex((self.currentIndex() - 1) % self.count()) class VerticalScrollArea(qw.QScrollArea): def __init__(self, parent): qw.QScrollArea.__init__(self, parent) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.horizontalScrollBar().setEnabled(False) # If removed, you will get unused space to the right and bottom. self.setWidgetResizable(True) # Only allow expanding, not shrinking. self.setSizePolicy(qsp(qsp.Minimum, qsp.Minimum))