154 lines
5.4 KiB
Python
154 lines
5.4 KiB
Python
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QLineEdit, QSpacerItem, QSpinBox, QDoubleSpinBox
|
|
from PyQt6.QtWidgets import QGridLayout, QMenu, QListWidget, QPushButton, QFormLayout, QDialog, QDialogButtonBox
|
|
from PyQt6.QtCore import pyqtSignal
|
|
|
|
"""
|
|
QWidget class that contains a variable amount of key - input pairs.
|
|
One pair per line in a dense layout
|
|
pairs. The value may be text - line edit, float - qdoublespinbox and int - qspinbox.
|
|
"""
|
|
|
|
class MetadataInput(QWidget):
|
|
metadataChanged = pyqtSignal()
|
|
def __init__(self, elements: list[tuple[str, str]]|dict[str,str]=None):
|
|
super().__init__()
|
|
# set layout
|
|
self.l_vbox = QVBoxLayout()
|
|
self.l_vbox.addWidget(QLabel("Measurement Metadata"))
|
|
self.l_grid = QGridLayout()
|
|
self.l_grid.setColumnMinimumWidth(0, 100)
|
|
self.l_grid.setColumnMinimumWidth(1, 100)
|
|
self.l_grid.setContentsMargins(0, 0, 0, 0)
|
|
self.l_grid.setSpacing(0)
|
|
# first row: key value <new element button>
|
|
self.l_grid.addWidget(QLabel("Key"), 0, 0)
|
|
self.l_grid.addWidget(QLabel("Value"), 0, 1)
|
|
self.btn_new_element = QPushButton("+")
|
|
self.btn_new_element.setFixedSize(20, 20)
|
|
self.btn_new_element.clicked.connect(self.add_element_dialog)
|
|
self.l_grid.addWidget(self.btn_new_element, 0, 2)
|
|
|
|
# key-value widgets
|
|
self.ws_elements = {}
|
|
if type(elements) == dict:
|
|
for (n, v) in elements.items():
|
|
self.add_element(n, v)
|
|
elif type(elements) == list:
|
|
for (n, v) in elements:
|
|
self.add_element(n, v)
|
|
self.l_grid.setContentsMargins(4, 4, 4, 4)
|
|
self.l_grid.setSpacing(4)
|
|
self.l_vbox.addLayout(self.l_grid)
|
|
self.setLayout(self.l_vbox)
|
|
self.layout_changed() # call even when no element was added
|
|
|
|
def layout_changed(self):
|
|
# add stretch to the last row
|
|
for r in range(self.l_grid.rowCount()):
|
|
self.l_grid.setRowStretch(r, 0)
|
|
self.l_grid.setRowStretch(self.l_grid.rowCount(), 1)
|
|
|
|
def add_element_dialog(self):
|
|
"""
|
|
Prompt for a new key-value pair using a Dialog having a form
|
|
"""
|
|
dialog = QDialog(self)
|
|
dialog.setWindowTitle("Add new field")
|
|
dialog.layout = QFormLayout()
|
|
dialog.key_input = QLineEdit()
|
|
dialog.value_input = QLineEdit()
|
|
dialog.layout.addRow("Key", dialog.key_input)
|
|
dialog.layout.addRow("Value", dialog.value_input)
|
|
|
|
# ok and cancel
|
|
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
|
|
buttons.accepted.connect(dialog.accept)
|
|
buttons.rejected.connect(dialog.reject)
|
|
dialog.layout.addRow(buttons)
|
|
dialog.setLayout(dialog.layout)
|
|
|
|
ret = dialog.exec()
|
|
if ret == QDialog.DialogCode.Accepted:
|
|
self.add_element(dialog.key_input.text(), dialog.value_input.text())
|
|
self.metadataChanged.emit()
|
|
|
|
def add_element(self, key, init_val=""):
|
|
if key in self.ws_elements:
|
|
raise RuntimeError(f"Can not add field '{key}', it already exists")
|
|
if key == "":
|
|
raise RuntimeError(f"Can not add field with empty key")
|
|
row = self.l_grid.rowCount()
|
|
w_label = QLabel(key)
|
|
self.l_grid.addWidget(w_label, row, 0)
|
|
|
|
w_input= QLineEdit(str(init_val))
|
|
w_input.editingFinished.connect(self.metadataChanged)
|
|
|
|
self.l_grid.addWidget(w_input, row, 1)
|
|
|
|
# Add a small button with an X from the theme
|
|
btn_remove = QPushButton("X")
|
|
btn_remove.setFixedSize(20, 20)
|
|
btn_remove.clicked.connect(lambda: self.remove_element(key))
|
|
self.l_grid.addWidget(btn_remove, row, 2)
|
|
|
|
self.ws_elements[key] = (w_label, w_input, btn_remove)
|
|
self.layout_changed()
|
|
|
|
def remove_element(self, name):
|
|
if name not in self.ws_elements:
|
|
raise RuntimeError(f"Can not remove field '{name}', it does not exist")
|
|
for w in self.ws_elements[name]:
|
|
self.l_grid.removeWidget(w)
|
|
del self.ws_elements[name]
|
|
self.layout_changed()
|
|
self.metadataChanged.emit()
|
|
|
|
|
|
def set_from_dict(self, d: dict[str, str]):
|
|
"""
|
|
Set the widgets from the dictionary
|
|
Parameters
|
|
----------
|
|
d
|
|
Dictionary of key-value pairs
|
|
Returns
|
|
-------
|
|
None
|
|
"""
|
|
# first remove widgets not in new dict
|
|
for key in self.ws_elements.keys():
|
|
if key not in d:
|
|
self.remove_element(key)
|
|
self.update_from_dict(d)
|
|
|
|
def update_from_dict(self, d: dict[str, str]):
|
|
"""
|
|
Update widgets from the dictionary.
|
|
New values will be added, no values will be removed
|
|
Parameters
|
|
----------
|
|
d
|
|
Dictionary of key-value pairs
|
|
Returns
|
|
-------
|
|
None
|
|
"""
|
|
for key, value in d.items():
|
|
if key not in self.ws_elements:
|
|
self.add_element(key, value)
|
|
else:
|
|
self.ws_elements[key][1].setText(value)
|
|
|
|
def get_dict(self):
|
|
"""
|
|
Returns
|
|
-------
|
|
Dictionary of all key-values pairs
|
|
"""
|
|
d = {}
|
|
for name, (w_label, w_input, _) in self.ws_elements.items():
|
|
key = w_label.text()
|
|
value = w_input.text()
|
|
d[key] = value
|
|
return d |