|
@@ -21,7 +21,7 @@ |
|
|
|
|
|
|
|
|
from collections import namedtuple |
|
|
from collections import namedtuple |
|
|
|
|
|
|
|
|
from PyQt5.QtCore import Qt, QRegExp |
|
|
|
|
|
|
|
|
from PyQt5.QtCore import Qt, QRegExp, pyqtSignal |
|
|
from PyQt5.QtGui import QRegExpValidator |
|
|
from PyQt5.QtGui import QRegExpValidator |
|
|
from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QHeaderView, QComboBox, QLineEdit, QSpinBox, QPushButton, QCheckBox, QHBoxLayout, QWidget |
|
|
from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QHeaderView, QComboBox, QLineEdit, QSpinBox, QPushButton, QCheckBox, QHBoxLayout, QWidget |
|
|
from shared import * |
|
|
from shared import * |
|
@@ -61,6 +61,8 @@ class BridgeSourceSink(QTableWidget): |
|
|
channels="2", |
|
|
channels="2", |
|
|
connected="True") |
|
|
connected="True") |
|
|
|
|
|
|
|
|
|
|
|
customChanged = pyqtSignal() |
|
|
|
|
|
|
|
|
def __init__(self, parent): |
|
|
def __init__(self, parent): |
|
|
QTableWidget.__init__(self, parent) |
|
|
QTableWidget.__init__(self, parent) |
|
|
self.bridgeData = [] |
|
|
self.bridgeData = [] |
|
@@ -79,7 +81,7 @@ class BridgeSourceSink(QTableWidget): |
|
|
# Name |
|
|
# Name |
|
|
name_col = QLineEdit() |
|
|
name_col = QLineEdit() |
|
|
name_col.setText(data.name) |
|
|
name_col.setText(data.name) |
|
|
name_col.returnPressed.connect(self.enable_buttons) |
|
|
|
|
|
|
|
|
name_col.textChanged.connect(self.customChanged.emit) |
|
|
rx = QRegExp("[^|]+") |
|
|
rx = QRegExp("[^|]+") |
|
|
validator = QRegExpValidator(rx, self) |
|
|
validator = QRegExpValidator(rx, self) |
|
|
name_col.setValidator(validator) |
|
|
name_col.setValidator(validator) |
|
@@ -100,21 +102,22 @@ class BridgeSourceSink(QTableWidget): |
|
|
combo_box.addItem(loudspeaker_icon, "sink") |
|
|
combo_box.addItem(loudspeaker_icon, "sink") |
|
|
|
|
|
|
|
|
combo_box.setCurrentIndex(0 if data.s_type == "source" else 1) |
|
|
combo_box.setCurrentIndex(0 if data.s_type == "source" else 1) |
|
|
combo_box.currentTextChanged.connect(self.enable_buttons) |
|
|
|
|
|
|
|
|
combo_box.currentTextChanged.connect(self.customChanged.emit) |
|
|
self.setCellWidget(row, 1, combo_box) |
|
|
self.setCellWidget(row, 1, combo_box) |
|
|
|
|
|
|
|
|
# Channels |
|
|
# Channels |
|
|
chan_col = QSpinBox() |
|
|
chan_col = QSpinBox() |
|
|
chan_col.setValue(int(data.channels)) |
|
|
chan_col.setValue(int(data.channels)) |
|
|
chan_col.setMinimum(1) |
|
|
chan_col.setMinimum(1) |
|
|
chan_col.valueChanged.connect(self.enable_buttons) |
|
|
|
|
|
|
|
|
chan_col.setAlignment(Qt.AlignCenter) |
|
|
|
|
|
chan_col.valueChanged.connect(self.customChanged.emit) |
|
|
self.setCellWidget(row, 2, chan_col) |
|
|
self.setCellWidget(row, 2, chan_col) |
|
|
|
|
|
|
|
|
# Auto connect? |
|
|
# Auto connect? |
|
|
auto_cb = QCheckBox() |
|
|
auto_cb = QCheckBox() |
|
|
auto_cb.setObjectName("auto_cb") |
|
|
auto_cb.setObjectName("auto_cb") |
|
|
auto_cb.setCheckState(Qt.Checked if data.connected in ['true', 'True', 'TRUE'] else Qt.Unchecked) |
|
|
auto_cb.setCheckState(Qt.Checked if data.connected in ['true', 'True', 'TRUE'] else Qt.Unchecked) |
|
|
auto_cb.stateChanged.connect(self.enable_buttons) |
|
|
|
|
|
|
|
|
auto_cb.stateChanged.connect(self.customChanged.emit) |
|
|
widget = QWidget() |
|
|
widget = QWidget() |
|
|
h_layout = QHBoxLayout(widget) |
|
|
h_layout = QHBoxLayout(widget) |
|
|
h_layout.addWidget(auto_cb) |
|
|
h_layout.addWidget(auto_cb) |
|
@@ -124,19 +127,15 @@ class BridgeSourceSink(QTableWidget): |
|
|
self.setCellWidget(row, 3, widget) |
|
|
self.setCellWidget(row, 3, widget) |
|
|
self.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) |
|
|
self.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) |
|
|
|
|
|
|
|
|
def enable_buttons(self): |
|
|
|
|
|
# Can't work out how to tell the table that data has changed (to cause the buttons to become enabled), |
|
|
|
|
|
# so instead manually make the buttons enabled. |
|
|
|
|
|
for btn_name in ["b_bridge_save", "b_bridge_undo"]: |
|
|
|
|
|
self.parent().findChild(QPushButton, btn_name).setProperty("enabled", True) |
|
|
|
|
|
|
|
|
|
|
|
def defaults(self): |
|
|
def defaults(self): |
|
|
self.bridgeData = [self.defaultPASourceData, self.defaultPASinkData] |
|
|
self.bridgeData = [self.defaultPASourceData, self.defaultPASinkData] |
|
|
self.load_data_into_cells() |
|
|
self.load_data_into_cells() |
|
|
|
|
|
self.customChanged.emit() |
|
|
|
|
|
|
|
|
def undo(self): |
|
|
def undo(self): |
|
|
self.load_from_settings() |
|
|
self.load_from_settings() |
|
|
self.load_data_into_cells() |
|
|
self.load_data_into_cells() |
|
|
|
|
|
self.customChanged.emit() |
|
|
|
|
|
|
|
|
def initialise_settings(self): |
|
|
def initialise_settings(self): |
|
|
GlobalSettings.setValue( |
|
|
GlobalSettings.setValue( |
|
@@ -147,14 +146,91 @@ class BridgeSourceSink(QTableWidget): |
|
|
bridgeDataText = GlobalSettings.value("Pulse2JACK/PABridges") |
|
|
bridgeDataText = GlobalSettings.value("Pulse2JACK/PABridges") |
|
|
self.bridgeData = self.decode_bridge_data(bridgeDataText) |
|
|
self.bridgeData = self.decode_bridge_data(bridgeDataText) |
|
|
|
|
|
|
|
|
|
|
|
def hasChanges(self)->bool: |
|
|
|
|
|
bridgeDataText = GlobalSettings.value("Pulse2JACK/PABridges") |
|
|
|
|
|
saved_data = self.decode_bridge_data(bridgeDataText) |
|
|
|
|
|
|
|
|
|
|
|
if self.rowCount() != len(saved_data): |
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
for row in range(self.rowCount()): |
|
|
|
|
|
orig_data = saved_data[row] |
|
|
|
|
|
|
|
|
|
|
|
name = self.cellWidget(row, 0).text() |
|
|
|
|
|
if name != orig_data[0]: |
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
type = self.cellWidget(row, 1).currentText() |
|
|
|
|
|
if type != orig_data[1]: |
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
channels = self.cellWidget(row, 2).value() |
|
|
|
|
|
if channels != int(orig_data[2]): |
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
auto_cb = self.cellWidget(row, 3).findChild(QCheckBox, "auto_cb") |
|
|
|
|
|
connected = auto_cb.isChecked() |
|
|
|
|
|
if connected != bool(orig_data[3]): |
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def hasValidValues(self)->bool: |
|
|
|
|
|
used_names = [] |
|
|
|
|
|
|
|
|
|
|
|
row_count = self.rowCount() |
|
|
|
|
|
# Prevent save without any bridge |
|
|
|
|
|
if not row_count: |
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
for row in range(row_count): |
|
|
|
|
|
line_edit = self.cellWidget(row, 0) |
|
|
|
|
|
name = line_edit.text() |
|
|
|
|
|
|
|
|
|
|
|
if not name or name in used_names: |
|
|
|
|
|
# prevent double name entries |
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
used_names.append(name) |
|
|
|
|
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
def add_row(self): |
|
|
def add_row(self): |
|
|
self.bridgeData.append(SSData(name="", s_type="source", channels="2", connected="False")) |
|
|
|
|
|
|
|
|
# first, search in table which bridge exists |
|
|
|
|
|
# to add the most pertinent new bridge |
|
|
|
|
|
has_source = False |
|
|
|
|
|
has_sink = False |
|
|
|
|
|
|
|
|
|
|
|
for row in range(self.rowCount()): |
|
|
|
|
|
cell_widget = self.cellWidget(row, 1) |
|
|
|
|
|
|
|
|
|
|
|
group_type = "" |
|
|
|
|
|
if cell_widget: |
|
|
|
|
|
group_type = cell_widget.currentText() |
|
|
|
|
|
|
|
|
|
|
|
if group_type == "source": |
|
|
|
|
|
has_source = True |
|
|
|
|
|
elif group_type == "sink": |
|
|
|
|
|
has_sink = True |
|
|
|
|
|
|
|
|
|
|
|
if has_source and has_sink: |
|
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
ss_data = SSData(name="", s_type="source", channels="2", connected="False") |
|
|
|
|
|
if not has_sink: |
|
|
|
|
|
ss_data = self.defaultPASinkData |
|
|
|
|
|
elif not has_source: |
|
|
|
|
|
ss_data = self.defaultPASourceData |
|
|
|
|
|
|
|
|
|
|
|
self.bridgeData.append(ss_data) |
|
|
self.load_data_into_cells() |
|
|
self.load_data_into_cells() |
|
|
self.editItem(self.item(self.rowCount() - 1, 0)) |
|
|
self.editItem(self.item(self.rowCount() - 1, 0)) |
|
|
|
|
|
self.customChanged.emit() |
|
|
|
|
|
|
|
|
def remove_row(self): |
|
|
def remove_row(self): |
|
|
del self.bridgeData[self.currentRow()] |
|
|
del self.bridgeData[self.currentRow()] |
|
|
self.load_data_into_cells() |
|
|
self.load_data_into_cells() |
|
|
|
|
|
self.customChanged.emit() |
|
|
|
|
|
|
|
|
def save_bridges(self): |
|
|
def save_bridges(self): |
|
|
self.bridgeData = [] |
|
|
self.bridgeData = [] |
|
@@ -177,6 +253,7 @@ class BridgeSourceSink(QTableWidget): |
|
|
# Need an extra line at the end |
|
|
# Need an extra line at the end |
|
|
conn_file.write("\n") |
|
|
conn_file.write("\n") |
|
|
conn_file.close() |
|
|
conn_file.close() |
|
|
|
|
|
self.customChanged.emit() |
|
|
|
|
|
|
|
|
# encode and decode from tuple so it isn't stored in the settings file as a type, and thus the |
|
|
# encode and decode from tuple so it isn't stored in the settings file as a type, and thus the |
|
|
# configuration is backwards compatible with versions that don't understand SSData types. |
|
|
# configuration is backwards compatible with versions that don't understand SSData types. |
|
|