Files
SwissMX/GenericDialog.py

205 lines
6.1 KiB
Python

import logging
_log = logging.getLogger(__name__)
import re
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QLineEdit, QTextEdit, QGridLayout, QVBoxLayout, QWidget, QDialog, QDialogButtonBox, QPushButton
from PyQt5.QtWidgets import QDoubleSpinBox, QSpinBox, QCheckBox, QLabel
def Spinner(value, min=None, max=None, decimals=None, single_step=None, prefix=None, suffix=None):
if type(value) is float:
s = QDoubleSpinBox()
else:
s = QSpinBox()
if min is not None:
s.setMinimum(min)
if max is not None:
s.setMaximum(max)
try:
if decimals is not None:
s.setDecimals(decimals)
except:
pass
if single_step is not None:
s.setSingleStep(single_step)
if prefix is not None:
s.setPrefix(prefix)
if suffix is not None:
s.setSuffix(suffix)
return s
def Checkbox(value: bool, label: str):
box = QCheckBox(label)
box.setChecked(value)
return box
class MagicLabel(QLabel):
entered = pyqtSignal(str)
left = pyqtSignal()
def __init__(self, label, help_str, help_buddy=None):
super(MagicLabel, self).__init__(label)
self._help_buddy = help_buddy
self._help_str = help_str
def enterEvent(self, event):
print(self._help_str)
if self._help_buddy:
print(f"yay!: {self._help_str}")
else:
super().enterEvent(event)
def leaveEvent(self, event):
super().leaveEvent(event)
class GenericDialog(QDialog):
def __init__(self, parent=None, settings=None, title=None, message=None, inputs={}, use_buttons=True):
super(GenericDialog, self).__init__()
self.settings = settings
# self.setModal(True)
self.setAccessibleName("genericDialog")
self.setLayout(QVBoxLayout())
bbox = QDialogButtonBox()
doneButton = QPushButton("Done")
doneButton.clicked.connect(self.accept)
bbox.addButton(doneButton, QDialogButtonBox.AcceptRole)
if use_buttons:
undoButton = QPushButton("Undo")
undoButton.clicked.connect(self.undo_all)
discardButton = QPushButton("Discard")
discardButton.clicked.connect(self.discard)
bbox.addButton(undoButton, QDialogButtonBox.ActionRole)
bbox.addButton(discardButton, QDialogButtonBox.RejectRole)
lb = QLabel(title)
lb.setAccessibleName("genericDialogTitle")
lb.setAlignment(Qt.AlignHCenter)
self.layout().addWidget(lb)
gbox = QWidget()
self.layout().addWidget(gbox)
gbox.setLayout(QVBoxLayout())
mlabel = QLabel(message)
mlabel.setAccessibleName("genericDialogMessage")
gbox.layout().addWidget(mlabel)
self.setWindowTitle(title)
self.contents = QWidget()
self.contents.setAccessibleName("genericDialogContents")
self.layout().addWidget(self.contents)
self.layout().addStretch()
self.layout().addWidget(bbox)
if not inputs:
inputs = {'test': ('Text test', 'initial', QLineEdit()),
'float': ('Text test 2', 3.14, QDoubleSpinBox(), 5.0),
'integer': ('Text test 2', 42, QSpinBox()),
}
self.contents.setLayout(QGridLayout())
layout = self.contents.layout()
layout.setColumnStretch(1, 2)
self.results = {}
self._undo = {}
for row, config in enumerate(inputs.items()):
key, config = config
config = list(config)
if len(config) == 3:
config.extend([""])
label, value, widget, help_str = config
labwig = MagicLabel(label, help_str)
layout.addWidget(labwig, row, 0)
layout.addWidget(widget, row, 1)
if hasattr(widget, 'setChecked'):
widget.setChecked(value)
widget.stateChanged.connect(lambda v, k=key, w=widget.isChecked: self.update_results(k, w))
elif hasattr(widget, 'setValue'):
widget.setValue(value)
widget.valueChanged.connect(lambda v, k=key, w=widget.value: self.update_results(k, w))
elif hasattr(widget, 'setPlainText'):
widget.setPlainText(value)
widget.textChanged.connect(lambda k=key, w=widget.toPlainText: self.update_results(k, w))
elif hasattr(widget, 'setText'):
widget.setText(value)
widget.editingFinished.connect(lambda k=key, w=widget.text: self.update_results(k, w))
self.results[key] = value
self._undo[key] = (widget, value)
def undo_all(self):
for k, uinfo in self._undo.items():
widget, value = uinfo
if hasattr(widget, 'setChecked'):
widget.setChecked(value)
elif hasattr(widget, 'setValue'):
widget.setValue(value)
elif hasattr(widget, 'setPlainText'):
widget.setPlainText(value)
elif hasattr(widget, 'setText'):
widget.setText(value)
def discard(self):
self.undo_all()
self.reject()
def set_value(self, widget, url):
m = re.match("^(.*?)://(.*)", url, re.MULTILINE|re.DOTALL)
method, value = m.groups()
if method == "setValue":
widget.setValue(float(value))
elif method == "setText":
widget.setText(value)
else:
widget.setPlainText(value)
if self.settings is not None:
self.settings.setValue(k, value)
def update_results(self, key, get_value):
value = get_value()
_log.debug(f"settigns {key} => {value}")
self.results[key] = value
if self.settings is not None:
self.settings.setValue(k, value)
def keyPressEvent(self, key_event):
if key_event != Qt.Key_Escape:
super().keyPressEvent(key_event)
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
title = 'A Lovely Title'
message = 'thisn sohudl explain what do tod ow with this dialog and in fact we should make sure that it will be wrapping aorufnnfb'
main = GenericDialog(title=title, message=message, inputs={
'bananas': ('Number of Bananas', 2, QSpinBox(), 10),
'apples': ('Number of Apples', 5.5, QDoubleSpinBox(), 23),
'really': ('Indeed?', True, Checkbox(True, "Contr")),
'words': ('Words', 'words go here', QLineEdit(), "a few words"),
'texts': ('Words', 'big words go here', QTextEdit(), """quite a drama\n to write here, really something awfull long"""),
})
if main.exec():
print(main.results)
for k, v in main.results.items():
print('{} {} = {}'.format(k, type(v), v))
sys.exit(app.exec_())