|
|
@@ -21,8 +21,9 @@ |
|
|
|
|
|
|
|
from collections import namedtuple |
|
|
|
|
|
|
|
from PyQt5.QtCore import Qt |
|
|
|
from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QHeaderView, QComboBox |
|
|
|
from PyQt5.QtCore import Qt, QRegExp |
|
|
|
from PyQt5.QtGui import QRegExpValidator |
|
|
|
from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QHeaderView, QComboBox, QLineEdit, QSpinBox, QPushButton |
|
|
|
from shared import * |
|
|
|
from shared_cadence import GlobalSettings |
|
|
|
|
|
|
@@ -52,13 +53,13 @@ class BridgeSourceSink(QTableWidget): |
|
|
|
name="PulseAudio JACK Source", |
|
|
|
s_type="source", |
|
|
|
channels="2", |
|
|
|
connected="true") |
|
|
|
connected="True") |
|
|
|
|
|
|
|
defaultPASinkData = SSData( |
|
|
|
name="PulseAudio JACK Sink", |
|
|
|
s_type="sink", |
|
|
|
channels="2", |
|
|
|
connected="true") |
|
|
|
connected="True") |
|
|
|
|
|
|
|
def __init__(self, parent): |
|
|
|
QTableWidget.__init__(self, parent) |
|
|
@@ -68,29 +69,48 @@ class BridgeSourceSink(QTableWidget): |
|
|
|
self.load_from_settings() |
|
|
|
|
|
|
|
def load_data_into_cells(self): |
|
|
|
self.setHorizontalHeaderLabels(['Name', 'Type', 'Channels', 'Auto Connect']) |
|
|
|
self.setHorizontalHeaderLabels(['Name', 'Type', 'Channels', 'Conn?']) |
|
|
|
self.setRowCount(0) |
|
|
|
|
|
|
|
for data in self.bridgeData: |
|
|
|
row = self.rowCount() |
|
|
|
self.insertRow(row) |
|
|
|
# convert to types from strings so the default editors can be used |
|
|
|
self.setItem(row, 0, self.create_widget_item(data.name)) |
|
|
|
self.setItem(row, 1, self.create_widget_item(data.s_type)) |
|
|
|
# self.setCellWidget(row, 1, self.create_combo_item(data.s_type)) |
|
|
|
self.setItem(row, 2, self.create_widget_item(int(data.channels))) |
|
|
|
self.setItem(row, 3, self.create_widget_item(data.connected in ['true', 'True', 'TRUE'])) |
|
|
|
self.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) |
|
|
|
|
|
|
|
def create_widget_item(self, i): |
|
|
|
item = QTableWidgetItem() |
|
|
|
item.setData(Qt.EditRole, i) |
|
|
|
return item |
|
|
|
|
|
|
|
def create_combo_item(self, v): |
|
|
|
comboBox = QComboBox() |
|
|
|
comboBox.addItems(["source", "sink"]) |
|
|
|
comboBox.setCurrentIndex(0 if v == "source" else 1) |
|
|
|
return comboBox |
|
|
|
|
|
|
|
# Name |
|
|
|
name_col = QLineEdit() |
|
|
|
name_col.setText(data.name) |
|
|
|
name_col.returnPressed.connect(self.enable_buttons) |
|
|
|
rx = QRegExp("[^|]+") |
|
|
|
validator = QRegExpValidator(rx, self) |
|
|
|
name_col.setValidator(validator) |
|
|
|
self.setCellWidget(row, 0, name_col) |
|
|
|
|
|
|
|
# Type |
|
|
|
combo_box = QComboBox() |
|
|
|
combo_box.addItems(["source", "sink"]) |
|
|
|
combo_box.setCurrentIndex(0 if data.s_type == "source" else 1) |
|
|
|
combo_box.currentTextChanged.connect(self.enable_buttons) |
|
|
|
self.setCellWidget(row, 1, combo_box) |
|
|
|
|
|
|
|
# Channels |
|
|
|
chan_col = QSpinBox() |
|
|
|
chan_col.setValue(int(data.channels)) |
|
|
|
chan_col.setMinimum(1) |
|
|
|
chan_col.valueChanged.connect(self.enable_buttons) |
|
|
|
self.setCellWidget(row, 2, chan_col) |
|
|
|
|
|
|
|
# Auto connect? |
|
|
|
cb = QTableWidgetItem() |
|
|
|
cb.setFlags(cb.flags() | Qt.ItemIsUserCheckable) |
|
|
|
cb.setCheckState(Qt.Checked if data.connected in ['true', 'True', 'TRUE'] else Qt.Unchecked) |
|
|
|
self.setItem(row, 3, cb) |
|
|
|
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): |
|
|
|
self.bridgeData = [self.defaultPASourceData, self.defaultPASinkData] |
|
|
@@ -110,27 +130,27 @@ class BridgeSourceSink(QTableWidget): |
|
|
|
self.bridgeData = self.decode_bridge_data(bridgeDataText) |
|
|
|
|
|
|
|
def add_row(self): |
|
|
|
self.bridgeData.append(SSData(name="", s_type="source", channels="2", connected="false")) |
|
|
|
self.bridgeData.append(SSData(name="", s_type="source", channels="2", connected="False")) |
|
|
|
self.load_data_into_cells() |
|
|
|
self.editItem(self.item(self.rowCount() - 1, 0)) |
|
|
|
|
|
|
|
def remove_row(self): |
|
|
|
currentRow = self.currentRow() |
|
|
|
del self.bridgeData[currentRow] |
|
|
|
del self.bridgeData[self.currentRow()] |
|
|
|
self.load_data_into_cells() |
|
|
|
|
|
|
|
def save_bridges(self): |
|
|
|
self.bridgeData = [] |
|
|
|
for row in range(0, self.rowCount()): |
|
|
|
new_name = self.item(row, 0).text() |
|
|
|
new_type = self.item(row, 1).text() |
|
|
|
new_channels = self.item(row, 2).text() |
|
|
|
new_conn = self.item(row, 3).text() |
|
|
|
new_name = self.cellWidget(row, 0).property("text") |
|
|
|
new_type = self.cellWidget(row, 1).currentText() |
|
|
|
new_channels = self.cellWidget(row, 2).value() |
|
|
|
new_conn = self.item(row, 3).checkState() == Qt.Checked |
|
|
|
|
|
|
|
self.bridgeData.append( |
|
|
|
SSData(name=new_name, |
|
|
|
s_type=new_type, |
|
|
|
channels=new_channels, |
|
|
|
connected=new_conn)) |
|
|
|
connected=str(new_conn))) |
|
|
|
GlobalSettings.setValue("Pulse2JACK/PABridges", self.encode_bridge_data(self.bridgeData)) |
|
|
|
conn_file_path = os.path.join(PULSE_USER_CONFIG_DIR, "jack-connections") |
|
|
|
conn_file = open(conn_file_path, "w") |
|
|
@@ -142,9 +162,14 @@ class BridgeSourceSink(QTableWidget): |
|
|
|
# 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. |
|
|
|
# Uses PIPE symbol as separator |
|
|
|
# TODO: fail if any pipes in names as it will break the encode/decode of the data |
|
|
|
def encode_bridge_data(self, data): |
|
|
|
return list(map(lambda s: s.name + "|" + s.s_type + "|" + str(s.channels) + "|" + str(s.connected), data)) |
|
|
|
|
|
|
|
def decode_bridge_data(self, data): |
|
|
|
return list(map(lambda d: SSData._make(d.split("|")), data)) |
|
|
|
|
|
|
|
def resizeEvent(self, event): |
|
|
|
self.setColumnWidth(0, int(self.width() * 0.5)) |
|
|
|
self.setColumnWidth(1, int(self.width() * 0.19)) |
|
|
|
self.setColumnWidth(2, int(self.width() * 0.19)) |
|
|
|
self.setColumnWidth(3, int(self.width() * 0.12)) |