# Copyright (C) 2012, SINTEF Materials and Chemistry
# (See accompanying license files for details).
#
# By Jesper Friis
"""
Editor for Parameters objects.
Examples
--------
Create a Parameters object to edit
>>> import pario.parameters
>>> p = pario.parameters.Parameters()
>>> p._set('velocity', 5, 'm/s', doc='out speed')
Easiest way to use paredit is through the _edit() method
>>> p._edit() #doctest: +SKIP
The same functionality is provided with :func:`pario.paredit.paredit`:
>>> paredit(p) #doctest: +SKIP
"""
from __future__ import absolute_import
import textwrap
from PyQt4 import QtCore, QtGui
import numpy as np
from . import parameters
from .arrayedit import ArrayEdit
[docs]def paredit(par, dialog=None, names=None, group=None, title='Parameters',
header=True, callback=None, wait=True):
"""Creates a parameter dialog and updates *par*.
Parameters
----------
par : Parameters instance
Parameters instance to edit.
dialog : None | ParEdit instance
If given, the existing *dialog* is made visible and raised.
instead of creating a new dialog.
names : None | sequence of strings
If given, include only parameters included in names.
group : None | string
If given, include only parameters of this group.
title : string
Dialog title.
header : bool
Whether to add a header.
callback : None | callable(par, done)
A callback function that is called when the dialog is closed
or the apply button is pressed. `callable` will be called
with two arguments:
par
updated parameter instance
done
a boolean that is true if the dialog is closed
wait : bool
If True, this function does not return before the dialog is
closed.
Returns
-------
QtGui.QDialog instance
"""
# Create a QApplication instance if no instance currently exists
# (e.g. if the module is used directly from the interpreter)
# It is important to globally store an reference to the application,
# otherwise the dialog wil not show up.
if QtGui.QApplication.startingUp():
global app
app = QtGui.QApplication([])
if dialog is None:
dialog = ParEdit(par, names=names, group=group, title=title,
header=header, callback=callback)
else:
dialog.update()
if wait:
#dialog.setWindowModality(QtCore.Qt.WindowModal)
dialog.exec_()
elif dialog.isVisible():
dialog.raise_()
dialog.activateWindow()
else:
dialog.show()
return dialog
#------------------------------------------------------------------
# Widgets showing the parameter value
#------------------------------------------------------------------
[docs]class ParEdit(QtGui.QDialog):
"""A class that allows graphical editing of model parameter.
See paredit() for a description of the arguments.
"""
def __init__(self, par, parent=None, names=None, group=None,
title='Parameters', header=True, callback=None):
QtGui.QDialog.__init__(self, parent)
self.par = par
self.header = header
self._callback = callback
if names is None:
if group is not None:
#names = [name for name in par._names
# if par._groups.get(name, None) == group]
names = par._get_names(group=group)
else:
names = par._names
self.names = names
self.widgets = {}
#self.editdialogs = {}
self.setupUI()
self.setWindowTitle(title)
#self.show()
def setupUI(self):
par = self.par
grid = QtGui.QGridLayout()
#grid.setSpacing(8)
has_doc = any([name in par._doc for name in par._names])
if self.header:
for i, name in enumerate(
('Parameter', 'Value', 'Unit', 'Description')):
if not has_doc and name == 'Description':
continue
label = QtGui.QLabel(name)
font = label.font()
font.setBold(True)
label.setFont(font)
grid.addWidget(label, 0, i)
for i, name in enumerate(self.names):
doc = par._doc.get(name, '')
grid.addWidget(QtGui.QLabel(name), i + 1, 0)
widget = None
if isinstance(par[name], np.ndarray):
widget = ArrayWidget(name, par[name])
elif isinstance(par[name], bool):
widget = BoolWidget(name, par[name])
elif doc and 'One of:' in doc:
doc, possibilities = par._doc[name].split('One of:')
widget = OneOfWidget(name, par[name], possibilities)
elif doc and 'Sum of:' in doc:
doc, possibilities = par._doc[name].split('Sum of:')
widget = SumOfWidget(name, par[name], possibilities)
else:
widget = LineWidget(name, par[name])
self.widgets[name] = widget
grid.addWidget(widget, i + 1, 1)
unit = par._units[name]
if not unit or unit == '-':
unit = ''
grid.addWidget(QtGui.QLabel(unit), i + 1, 2)
if doc:
label = QtGui.QLabel(doc)
label.setWordWrap(True)
grid.addWidget(label, i + 1, 3)
scrollwidget = QtGui.QWidget()
scrollwidget.setLayout(grid)
#scrollwidget.adjustSize()
#scrollwidget.setSizeIncrement(20, 20)
#size.setWidth(size.width() + 20)
#size.setHeight(size.height() + 20)
#scrollwidget.setSize(size)
#size = scrollwidget.baseSize()
#print('*** basesize:', size.width(), size.height())
scrollarea = QtGui.QScrollArea()
scrollarea.setWidget(scrollwidget)
#scrollarea.setMinimumWidth(scrollarea.minimumWidth() + 20)
#scrollarea.setMinimumWidth(700)
#scrollarea.resize(size.width() + 120, size.height() + 120)
#scrollarea.adjustSize()
buttonbox = QtGui.QHBoxLayout()
saveButton = QtGui.QPushButton("Save")
okButton = QtGui.QPushButton("OK")
applyButton = QtGui.QPushButton("Apply")
cancelButton = QtGui.QPushButton("Cancel")
self.connect(saveButton, QtCore.SIGNAL("clicked()"), self.onSave)
self.connect(okButton, QtCore.SIGNAL("clicked()"), self.onOK)
self.connect(applyButton, QtCore.SIGNAL("clicked()"), self.onApply)
self.connect(cancelButton, QtCore.SIGNAL("clicked()"), self.onCancel)
buttonbox.addWidget(saveButton)
buttonbox.addStretch(1)
buttonbox.addWidget(okButton)
buttonbox.addWidget(applyButton)
buttonbox.addWidget(cancelButton)
vbox = QtGui.QVBoxLayout()
vbox = QtGui.QVBoxLayout()
#vbox.addSpacing(10)
#vbox.addStretch(1)
vbox.addWidget(scrollarea)
vbox.addLayout(buttonbox)
self.setLayout(vbox)
# Increase width to make horizontal scrollbar disappear
size = self.sizeHint()
size.setWidth(size.width() + 140)
self.resize(size)
# Keybindings: "Ctrl+Q" -> Cancel and "Ctrl+W" -> OK
cancelAction = QtGui.QAction(self)
cancelAction.setShortcut('Ctrl+Q')
cancelAction.triggered.connect(self.onCancel)
okAction = QtGui.QAction(self)
okAction.setShortcut('Ctrl+W')
okAction.triggered.connect(self.onOK)
[docs] def setpar(self):
"""Set parameters to new values."""
for name in self.names:
self.par[name] = self.widgets[name].get()
[docs] def update(self):
"""Update dialog from parameters."""
for name in self.names:
self.widgets[name].set(self.par[name])
def onOK(self):
self.onApply()
self.accept()
def onCancel(self):
if self._callback is not None:
self._callback(self.par, False)
self.reject()
def onApply(self):
self.setpar()
if self._callback is not None:
self._callback(self.par, True)
def onSave(self):
# with pyside, call instead QtGui.QFileDialog.getSaveFileName()
filename, filter = QtGui.QFileDialog.getSaveFileNameAndFilter(
parent=self,
caption='Save to file')
filename = str(filename)
if filename:
self.par._write(str(filename))
#def onEditTable(self, name):
# if name in self.editdialogs:
# if not self.editdialogs[name].isVisible():
# self.editdialogs[name].show()
# self.editdialogs[name].activateWindow()
# self.editdialogs[name].raise_()
# else:
# def callback(arr, done):
# self.par[name] = arr
# if done:
# del self.editdialogs[name]
# dialog = ArrayEdit(self.par[name], title=name,
# callback=callback)
# self.editdialogs[name] = dialog
# dialog.show()
if __name__ == '__main__':
import sys
p = parameters.fromstring('''
1 - model model selection. One of: 0=first model, 1=second model, 2=third model
10 - flags model flags. Sum of: 1=flag1, 2=flag2, 4=flag3, 8=flag4, 16=flag5
1.52e10 m^-2 rho_i dislocation density
# a comment
0.3e-6 m delta subgrain size
#------------------------------------------
# TREATMENT TABLE
# time(s) T(C) Css PZ(Pa)
0.0 330. 5.3138E-3 0.0
1.0 330. 5.3138E-3 0.0
5.0 330. 5.3138E-3 0.0
10.0 330. 5.3138E-3 0.0
''')
r = p.TREATMENT_TABLE
print('=' * 60)
print(p)
paredit(p)
print('=' * 60)
print(p)
print('=' * 60)
settings = parameters.Parameters()
settings.advanced = True
settings.name = 'abc'
settings.myint = 42
settings.mufloat = 3.14
#settings._show()
paredit(settings, title='Settings', header=False)
print(settings)