"""Panel containing buttons to run reduction steps"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import traceback
from PyQt4 import QtGui
try:
from PyQt4.QtCore import QString
except ImportError: # For Python3
QString = str
import six
from vdat.gui.queue import get_queue
from vdat.gui.fplane import yield_selected_ifus
import vdat.command_interpreter as vci
import vdat.config as vconf
import vdat.database as vdb
[docs]class ButtonsMenu(QtGui.QStackedWidget):
"""Parent widget containing the buttons to run the reduction steps
Parameters
----------
parent : :class:`QtWidget` instance
The QtWidget that the menu is attached to
name : string
name of the widget
Attributes
----------
"""
def __init__(self, parent=None, name="buttons_widget"):
super(ButtonsMenu, self).__init__(parent=parent)
# map each of the sub widget containing groups of buttons
self._subwidgets_map = {}
self.setObjectName(name)
# create an empty widget to hide the other ones when nothing has been
# selected
blank_widget = QtGui.QWidget(parent=self)
blank_widget.setObjectName('blank')
blank_index = self.addWidget(blank_widget)
self.setCurrentIndex(blank_index)
self.setMinimumWidth(100)
self.setMaximumWidth(300)
[docs] def get_subwidget(self, name):
"""Returns a sub-widget called ``name``. Create it if needed"""
try:
return self._subwidgets_map[name]
except KeyError:
subwidget = ButtonWidget(parent=self, name=name)
self._subwidgets_map[name] = subwidget
self.addWidget(subwidget)
return subwidget
[docs] def setCurrentWidgetByName(self, name):
"""Set the current widget by name
Parameters
----------
name : string
name associated with the widget
"""
widget = self._subwidgets_map[name]
super(ButtonsMenu, self).setCurrentWidget(widget)
[docs]class ButtonWidget(QtGui.QWidget):
"""Actual widget containing the buttons
Parameters
----------
parent : :class:`QtWidget` instance
The QtWidget that the menu is attached to
name : string
name of the widget
"""
def __init__(self, parent=None, name=''):
super(ButtonWidget, self).__init__(parent=parent)
# map each of the sub widget containing groups of buttons
self._button_map = {}
self.name = name
self.setObjectName(name)
self.layout = QtGui.QVBoxLayout()
self.layout.setObjectName(name + "layout")
self.setLayout(self.layout)
[docs] def add_button(self, name, commands, tool_tip=None):
"""Adds a button to the widget.
Parameters
----------
name : string
name of the button
commands : list of strings, optional
strings defining the commands
tool_tip : string, optional
text that appears as a tool tip when the user hovers their mouse
over the button
"""
button = CommandButton(name, commands, tool_tip=tool_tip, parent=self)
# Save it in case we need it later
self._button_map[name] = button
self.layout.addWidget(button)
[docs] def add_stretch(self, stretch=1):
"""Add a stretch to the layout containing the buttons
Parameters
----------
stretch : int, optional
stretch factor
"""
self.layout.addStretch(stretch)
[docs]class CommandButton(QtGui.QPushButton):
"""Custom class implementing the button.
Parameters
----------
name : string
name of the button
commands : list of strings, optional
strings defining the commands
tool_tip : string, optional
text that appears as a tool tip when the user hovers their mouse
over the button
parent : :class:`QtWidget` instance
The QtWidget that the menu is attached to
Attributes
----------
commands
"""
def __init__(self, name, commands, tool_tip=None, parent=None):
super(CommandButton, self).__init__(parent=parent)
if isinstance(commands, six.string_types):
commands = [commands]
self.commands = commands
self.command_names = [c.split()[0] for c in self.commands]
if parent:
object_name = parent.objectName() + name
else:
object_name = name
self.setObjectName(object_name)
self.setText(name)
if tool_tip is not None:
self.setToolTip(tool_tip)
self.clicked.connect(self._callback())
[docs] def _callback(self):
def make_commands():
submit_to_queue = []
ihmpids = self._selected_ifus()
if not ihmpids:
return
for name, command in zip(self.command_names, self.commands):
try:
command_conf = self._config_with_dirs(name)
cmd_interpreter = vci.CommandInterpreter(command,
command_conf,
selected=ihmpids)
submit_to_queue.append([cmd_interpreter, name, command])
except Exception as e:
self._error_dialog(e, command)
return
queue = get_queue()
for sq in submit_to_queue:
queue.add_command(*sq)
return make_commands
[docs] def _config_with_dirs(self, command_name):
"""Get the command configuration and add ``target_dir``, ``zero_dir``
and ``cal_dir`` to the configuration.
Parameters
----------
command_name : string
name of the command
Returns
-------
command_conf : dict
configuration dictionary for the command at hand
"""
command_conf = vconf.get_config('commands', section=command_name)
dirs_conf = vconf.get_config('main', section='redux_dirs')
# the target, or selected, directory is selected
command_conf['target_dir'] = dirs_conf['selected_dir']
# if no calibration and/or zero directory are selected, get them from
# the database
db_entry = (vdb.VDATDir.select()
.where(vdb.VDATDir.path == command_conf['target_dir'])
.get())
command_conf['zero_dir'] = dirs_conf.get('zro_dir',
getattr(db_entry.zero_dir,
'path', ''))
command_conf['cal_dir'] = dirs_conf.get('cal_dir',
getattr(db_entry.cal_dir,
'path', ''))
return command_conf
[docs] def _error_dialog(self, error, command):
"""Create the dialog to show the error
Parameters
----------
error : :class:`Exception` instance
error raised by the constructor
command : string
full command string
"""
box = QtGui.QMessageBox(parent=self)
text = ("The following exceptions has been raised while"
" parsing the command: '{}'\n:{}".format(command, error))
box.setText(text)
if isinstance(error, (vci.exceptions.CIError,
vconf.MissingConfigurationSectionError)):
informative_text = ('Make sure that the command configuration'
' file and the command that you want to run'
' are in sync')
icon = QtGui.QMessageBox.Warning
else:
informative_text = ("The error is unexpected and it is probably a"
" bug. If you want to inspect the error click"
" on the 'Show details' button to see the full"
" traceback. If you want to report the error"
" to the developers, please ")
icon = QtGui.QMessageBox.Critical
box.setInformativeText(informative_text)
box.setIcon(icon)
tb_text = traceback.format_exc()
box.setDetailedText(tb_text)
box.exec_()
[docs] def _selected_ifus(self):
"""Collect the selected IFUs and return a list of ifu
Return
------
ihmpid : list
list of ifu head mount plate IDs
"""
ihmpids = [ifu.ihmpid for ifu in yield_selected_ifus()]
if ihmpids:
return ihmpids
else:
box = QtGui.QMessageBox(parent=self)
box.setText("Please select the IFUs by clicking on them or via "
" the 'Select' menu")
box.setIcon(QtGui.QMessageBox.Warning)
box.exec_()
[docs]def setup_buttons(parent=None):
"""Set up and return the buttons
Parameters
----------
parent : :class:`PyQt4.QtGui.QWidget`
parent of the button widget
Returns
-------
:class:`ButtonsMenu`
button widgets container
"""
button_config = vconf.get_config('buttons')
buttons_menu = ButtonsMenu(parent)
# loop through the entries in the configuration file
for k, v in six.iteritems(button_config):
b_widget = buttons_menu.get_subwidget(k)
# the value ``v`` is a list of buttons
for button in v:
b_widget.add_button(**button)
# Push the buttons to the top of the layout
b_widget.add_stretch()
return buttons_menu