Source code for vdat.gui.tasks
# Virus Data Analysis Tool: a data reduction GUI for HETDEX/VIRUS data
# Copyright (C) 2016, 2017 "The HETDEX collaboration"
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from qtpy import QtCore, QtWidgets
import six
import vdat.config as vconf
[docs]class VDATStack(QtWidgets.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, cls, parent=None, name="tasks_stack"):
super(VDATStack, 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
self.blank_widget = QtWidgets.QWidget(parent=self)
self.blank_widget.setObjectName('blank')
blank_index = self.addWidget(self.blank_widget)
self.setCurrentIndex(blank_index)
if cls is None:
raise Exception('Class to be stacked must be specified!')
self.cls = cls
[docs] def get_subwidget(self, name):
"""Returns a sub-widget called ``name``. Create it if needed"""
try:
return self._subwidgets_map[name], False
except KeyError:
subwidget = self.cls(parent=self)
self._subwidgets_map[name] = subwidget
self.addWidget(subwidget)
return subwidget, True
[docs] def setCurrentWidgetByName(self, name):
"""Set the current widget by name
Parameters
----------
name : string
name associated with the widget
"""
if name in self._subwidgets_map:
widget = self._subwidgets_map[name]
else:
widget = self.blank_widget
super(VDATStack, self).setCurrentWidget(widget)
return widget
[docs]class VDATTaskStack(VDATStack):
"""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
----------
"""
sig_taskChanged = QtCore.Signal(str, dict)
sig_runCommand = QtCore.Signal(str, list)
def __init__(self, parent=None, name="tasks_stack"):
super(VDATTaskStack, self).__init__(VDATTaskWidget, parent=parent,
name=name)
[docs] def get_subwidget(self, name):
"""Returns a sub-widget called ``name``. Create it if needed"""
subwidget, new = super(VDATTaskStack, self).get_subwidget(name)
if new:
subwidget.sig_selected.connect(self.taskSelected)
subwidget.sig_command.connect(self.commandTriggered)
return subwidget
[docs] def setCurrentWidgetByName(self, name):
"""Set the current widget by name
Parameters
----------
name : string
name associated with the widget
"""
try:
widget = super(VDATTaskStack, self).setCurrentWidgetByName(name)
widget.selectFirst()
except AttributeError:
raise TaskError('No task defined!')
[docs]class VDATButtonStack(VDATStack):
"""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
----------
"""
sig_runCommand = QtCore.Signal(int, list)
def __init__(self, parent=None, name="tasks_stack"):
super(VDATButtonStack, self).__init__(VDATButtonWidget, parent=parent,
name=name)
[docs] def get_subwidget(self, name):
"""Returns a sub-widget called ``name``. Create it if needed"""
subwidget, new = super(VDATButtonStack, self).get_subwidget(name)
if new:
subwidget.triggered.connect(self.commandTriggered)
return subwidget
[docs]class VDATTaskWidget(QtWidgets.QWidget):
sig_selected = QtCore.Signal(str, dict)
sig_command = QtCore.Signal(str, list)
def __init__(self, **kwargs):
super(VDATTaskWidget, self).__init__(**kwargs)
self.buttongroup = QtWidgets.QButtonGroup(parent=self)
self.buttongroup.setExclusive(True)
self.widget = QtWidgets.QWidget()
self.blayout = QtWidgets.QVBoxLayout(self)
self.blayout.setSpacing(0)
self.blayout.setContentsMargins(0, 0, 0, 0)
self.widget.setLayout(self.blayout)
self.scrollarea = QtWidgets.QScrollArea()
self.scrollarea.setWidgetResizable(True)
self.scrollarea.setHorizontalScrollBarPolicy(
QtCore.Qt.ScrollBarAlwaysOff)
self.scrollarea.setWidget(self.widget)
self.btnStack = VDATStack(VDATButtonWidget, self)
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.scrollarea)
self.layout.setStretchFactor(self.scrollarea, 10)
self.layout.addWidget(self.btnStack)
self.layout.setStretchFactor(self.btnStack, 1)
self.setLayout(self.layout)
self.buttongroup.buttonClicked[int].connect(self.buttonSelected)
self._tasks = {}
self._task_dicts = {}
self.maxID = 1
[docs] def add_task(self, task_dict, 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
"""
name = task_dict['step_name']
btn = QtWidgets.QPushButton(name, parent=self)
btn.setFlat(True)
btn.setCheckable(True)
if tool_tip is not None:
btn.setToolTip(tool_tip)
self.blayout.addWidget(btn)
self.buttongroup.addButton(btn, self.maxID)
self._tasks[self.maxID] = name
self._task_dicts[self.maxID] = task_dict
line = QtWidgets.QFrame(parent=self)
line.setFrameShape(QtWidgets.QFrame.HLine)
line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.blayout.addWidget(line)
bstk = None
if 'buttons' in task_dict:
bstk, new = self.btnStack.get_subwidget(name)
for b in task_dict['buttons']:
tip = None
if 'tool_tip' in b:
tip = b['tool_tip']
if new:
btn = bstk.add_button(b['name'], b['commands'],
tool_tip=tip)
bstk.sig_runCommand.connect(self.commandTriggered)
self.maxID += 1
return btn, bstk
[docs] def add_stretch(self, stretch=1):
"""Add a stretch to the layout containing the buttons
Parameters
----------
stretch : int, optional
stretch factor
"""
# Add a strech to keep buttons on top
self.blayout.addStretch()
[docs] @QtCore.Slot(int)
def buttonSelected(self, i):
'''
Slot to catch the the button pressed from the button group,
and re-emit with updated information
'''
self.btnStack.setCurrentWidgetByName(self._tasks[i])
self.sig_selected.emit(self._tasks[i], self._task_dicts[i])
[docs] @QtCore.Slot(str, list)
def commandTriggered(self, s, l):
'''
Slot to catch the button pressed from the button group,
and re-emit with updated information
'''
self.sig_command.emit(s, l)
[docs]class VDATButtonWidget(QtWidgets.QWidget):
sig_runCommand = QtCore.Signal(str, list)
def __init__(self, **kwargs):
super(VDATButtonWidget, self).__init__(**kwargs)
self.buttongroup = QtWidgets.QButtonGroup(parent=self)
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.layout)
self.buttongroup.buttonClicked[int].connect(self.clicked)
self._cmds = {}
self._desc = {}
self.maxID = 1
[docs] def add_button(self, name, command, 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
"""
if isinstance(command, six.string_types):
command = [command, ]
btn = QtWidgets.QPushButton(name, parent=self)
if tool_tip is not None:
btn.setToolTip(tool_tip)
self.layout.addWidget(btn)
self.buttongroup.addButton(btn, self.maxID)
self._desc[self.maxID] = name
self._cmds[self.maxID] = command
self.maxID += 1
return btn
[docs] @QtCore.Slot(int)
def clicked(self, i):
self.sig_runCommand.emit(self._desc[i], self._cmds[i])
[docs]def setup_tasks(parent=None):
"""Set up and return the buttons
Parameters
----------
parent : :class:`PyQt5.QtWidgets.QWidget`
parent of the button widget
Returns
-------
:class:`ButtonsMenu`
button widgets container
"""
task_config = vconf.get_config('tasks')
tasks = VDATTaskStack(parent)
# loop through the entries in the configuration file
for k, v in six.iteritems(task_config):
t_widget = tasks.get_subwidget(k)
# the value ``v`` is a list of buttons
for step in v:
# btns = None
# if 'buttons' in step:
# btns = step['buttons']
b, c = t_widget.add_task(step)
# b.clicked.connect(t_widget.)
# Push the buttons to the top of the layout
t_widget.add_stretch()
return tasks