Source code for pySPACE.run.gui.general_gui
""" Rough basic interface to run pySPACE with node chains """
import logging
import os
import sys
# import for documentation generation
from threading import Thread
import time
file_path = os.path.dirname(os.path.abspath(__file__))
pyspace_path = file_path[:file_path.rfind('pySPACE')-1]
if not pyspace_path in sys.path:
sys.path.append(pyspace_path)
import pySPACE
from pySPACE import create_operation_from_file, create_operation, create_backend
try:
from PyQt4 import QtGui, QtCore
except:
pass
if __name__ == "__main__":
#### Find pySPACE package and import it ####
# Determine path of current file
path = os.path.abspath(__file__)
# Move up to parent directory that contains the pySPACE tree
suffix = []
for i in range(4):
path, tail = os.path.split(path)
suffix.append(tail)
parent_dir = path
# Check proper directory structure
if suffix != ['general_gui.py', 'gui','run', 'pySPACE']:
raise RuntimeError, "Encountered incorrect directory structure. " \
"general_gui.py needs to reside in $PARENT_DIR/pySPACE/run/gui"
# Append pySPACE root directory to PYTHONPATH
sys.path.append(parent_dir)
#########################################
try:
from PyQt4 import QtGui, QtCore, QtWebKit
except ImportError:
import warnings
warnings.warn("ERROR: This GUI requires the PyQt4 package. You may use "\
"launch.py instead however.")
sys.exit(0)
import pySPACE
[docs]class PySpaceGui(QtGui.QMainWindow):
"""The main window of the GUI"""
[docs] def __init__(self, parent=None):
super(PySpaceGui, self).__init__(parent)
self.setWindowTitle('YAML based Signal Processing And Classification Environment written in Python (pySPACE)')
self.resize(1024, 768)
[docs] def set_up(self):
""" Setting up of GUI components """
# Lazy import of widgets
# Putting everything together
self.tabWidget = QtGui.QTabWidget(self)
self.tabWidget.setTabsClosable(True)
self.connect(self.tabWidget, QtCore.SIGNAL('tabCloseRequested (int)'),
self._closeTab)
# The tab for configuring and controlling operations
self.ControlTab = ControlWidget(self)
self.tabWidget.addTab(self.ControlTab, "Control")
# The tabs that show the documentation
# TODO: Currently, it is not possible to login via the viewer such that
# the desired page can not be displayed. Thus, we use the local
# documentation
DocumentationTab = QtWebKit.QWebView(self)
DocumentationTab.load(QtCore.QUrl("../../../docs/.build/html/index.html"))
self.tabWidget.addTab(DocumentationTab, "pySPACE Documentation")
self.connect(DocumentationTab.page().networkAccessManager(),
QtCore.SIGNAL("sslErrors (QNetworkReply *, const QList<QSslError> &)"),
self._sslErrorHandler)
# Set initially selected tab
self.tabWidget.setCurrentWidget(self.ControlTab)
self.setCentralWidget(self.tabWidget)
# Connect handler for signal that signals that an operation has been finished
self.ControlTab.operationFinishedSignal.connect(self._operation_finished)
# Implement proper shutdown
self.connect(self, QtCore.SIGNAL('quit()'), self._close_event)
[docs] def _operation_finished(self, result_file_name):
# Called whenever an operation is finished
from pySPACE.run.gui.performance_results_analysis import PerformanceResultsAnalysisWidget
# Convert special string "None" to object None
if result_file_name == "None": result_file_name = None
# Open results analysis tab
resultsAnalysisTab = \
PerformanceResultsAnalysisWidget(results_file=result_file_name,
parent=self)
self.tabWidget.addTab(resultsAnalysisTab, "Result Analysis")
self.tabWidget.setCurrentWidget(resultsAnalysisTab)
[docs] def _sslErrorHandler(self, reply, errorList):
# Ignore all SSL errors....
reply.ignoreSslErrors()
print "SSL error ignored: %s" % errorList[0].errorString()
[docs] def _closeTab(self, tabIndex):
# The control and documentation tabs must not be closed!
if tabIndex <= 2: return
self.tabWidget.widget(tabIndex).close()
self.tabWidget.removeTab(tabIndex)
if __name__ == '__main__':
#Creating Qt application
app = QtGui.QApplication(sys.argv)
gui = PySpaceGui()
# Let user select a configuration file
config_file = \
str(QtGui.QFileDialog.getOpenFileName(gui, "Select a configuration file ",
os.sep.join([parent_dir, "docs","examples", "conf"]),
"configuration files (*.yaml)"))
config_file = config_file.split(os.sep)[-1]
# Load configuration file
import pySPACE
pySPACE.load_configuration(config_file)
gui.set_up()
gui.show()
#Initing application
sys.exit(app.exec_())
[docs]class ControlWidget(QtGui.QWidget):
""" Widget for configuring a node chain operation and monitor its progress. """
# Two signals that are used to provide the GUI with the status of the
# running operation.
operationStatusChangedSignal = \
QtCore.pyqtSignal(int, name='operationStatusChanged')
operationFinishedSignal = \
QtCore.pyqtSignal(basestring, name='operationFinished')
[docs] def __init__(self, results_file=None, parent=None):
super(ControlWidget, self).__init__(parent)
self.operation = None
# hlayoutBackend
hlayoutBackend = QtGui.QHBoxLayout()
backendLabel = QtGui.QLabel("Backend")
self.backendComboBox = QtGui.QComboBox(self)
self.backendComboBox.addItems(["serial", "mcore", "mpi"])
self.backendComboBox.setToolTip("Select the backend")
hlayoutBackend.addWidget(backendLabel)
hlayoutBackend.addWidget(self.backendComboBox)
# hlayoutOperationType
hlayoutOperationType = QtGui.QHBoxLayout()
operationTypeLabel = QtGui.QLabel("Operation")
self.operationTypeComboBox = QtGui.QComboBox(self)
self.operationTypeComboBox.addItems(["Node Chain Operation"])
self.operationTypeComboBox.setToolTip("Select the type of operation")
opConfigButton = QtGui.QPushButton("&Create")
self.connect(opConfigButton, QtCore.SIGNAL('clicked()'),
self._config_node_chain)
opLoadButton = QtGui.QPushButton("&Load")
self.connect(opLoadButton, QtCore.SIGNAL('clicked()'),
self._load_node_chain)
hlayoutOperationType.addWidget(operationTypeLabel)
hlayoutOperationType.addWidget(self.operationTypeComboBox)
hlayoutOperationType.addWidget(opConfigButton)
hlayoutOperationType.addWidget(opLoadButton)
# start button
startButton = QtGui.QPushButton("&Start Operation")
self.connect(startButton, QtCore.SIGNAL('clicked()'),
self._start)
# load operation button
loadResultsButton = QtGui.QPushButton("&Load Operation Results")
self.connect(loadResultsButton, QtCore.SIGNAL('clicked()'),
self._loadResults)
# control bar
hlayoutControl = QtGui.QHBoxLayout()
hlayoutControl.addWidget(startButton)
hlayoutControl.addWidget(loadResultsButton)
# the log viewer
logViewer = LogViewer(self)
# operation progress
hlayoutProgress = QtGui.QHBoxLayout()
progressLabel = QtGui.QLabel("Operation progress")
self.progressBar = QtGui.QProgressBar()
self.etaLabel = \
QtGui.QLabel("ETA: --:--:--")
self.operationStatusChangedSignal.connect(self._update_progress_bar)
hlayoutProgress.addWidget(progressLabel)
hlayoutProgress.addWidget(self.progressBar)
hlayoutProgress.addWidget(self.etaLabel)
# Create the main layout of widget
vlayout = QtGui.QVBoxLayout()
vlayout.addLayout(hlayoutBackend)
vlayout.addLayout(hlayoutOperationType)
vlayout.addLayout(hlayoutControl)
vlayout.addWidget(logViewer)
vlayout.addLayout(hlayoutProgress)
self.setLayout(vlayout)
[docs] def _config_node_chain(self):
# Launch window that allows to load/configure a node chain operation
node_chain_config_frame = OperationConfigurationPopup(self)
[docs] def _load_node_chain(self):
# Let the user select an operation
operation_file = \
QtGui.QFileDialog.getOpenFileName(self, "Select an operation spec file",
pySPACE.configuration.spec_dir + os.sep + "operations",
"operation specification files (*.yaml)")
if operation_file == None:
return
# Create operation
self.operation = create_operation_from_file(str(operation_file))
[docs] def _start(self):
# Set number of processes in operation
self.progressBar.setMinimum(0)
self.progressBar.setMaximum(self.operation.number_processes)
self.progressBar.setValue(0)
# Create backend
self.backend = create_backend(str(self.backendComboBox.currentText()))
# Run operation in a separate thread to keep GUI responsive
self.operationThread = Thread(target=pySPACE.run_operation,
args=(self.backend, self.operation))
self.operationThread.start()
# Update progress bar in a separate thread
self.progressBarThread = Thread(target=self._monitor_progress, args=())
self.progressBarThread.start()
[docs] def _monitor_progress(self):
while self.operationThread.isAlive():
self.operationStatusChangedSignal.emit(self.backend.current_process)
time.sleep(0.1)
self.operationStatusChangedSignal.emit(self.backend.current_process)
self.operationFinishedSignal.emit(self.operation.result_directory + os.sep + "results.csv")
[docs] def _update_progress_bar(self, processesFinished):
self.progressBar.setValue(processesFinished)
if hasattr(self.backend, "progress_bar"):
# Compute Estimated Time of Arrival
pbar = self.backend.progress_bar
if pbar.currval == 0:
self.etaLabel.setText("ETA: --:--:--")
elif pbar.finished:
self.etaLabel.setText(time.strftime('%H:%M:%S', time.gmtime(pbar.seconds_elapsed)))
else:
elapsed = pbar.seconds_elapsed
eta = elapsed * pbar.maxval / pbar.currval - elapsed
self.etaLabel.setText("ETA: %s"
% time.strftime('%H:%M:%S', time.gmtime(eta)))
[docs] def _loadResults(self):
# Load results of an already executed operation
# This is done by pretending that an operation was finished but without
# a results file. This causes the results analysis window to ask for the
# location of the csv file.
self.operationFinishedSignal.emit("None")
[docs]class OperationConfigurationPopup(QtGui.QMainWindow):
""" Window for configuring a NodeChainOperation.
Allows to select an input and a node chain. Additionally, the window
allows to invoke the configuration GUI.
"""
[docs] def __init__(self, main_window, *args, **kwargs):
super(OperationConfigurationPopup, self).__init__(main_window)
self.main_window = main_window
self.selected_input = None
self.resize(640,480)
self.setWindowTitle('Operation Configuration')
self.central_widget = QtGui.QWidget()
# hlayoutData
hlayoutData = QtGui.QHBoxLayout()
self.DataLabel = QtGui.QLabel("Input: %s" % self.selected_input)
DataButton = QtGui.QPushButton("&Select")
self.connect(DataButton, QtCore.SIGNAL('clicked()'),
self._select_input)
hlayoutData.addWidget(self.DataLabel)
hlayoutData.addWidget(DataButton)
# hlayoutNodeChain
hlayoutNodeChain = QtGui.QHBoxLayout()
NodeChainLabel = QtGui.QLabel("Node Chain")
self.NodeChainComboBox = QtGui.QComboBox(self)
self.NodeChainComboBox.addItems(os.listdir(pySPACE.configuration.spec_dir + os.sep + "node_chains"))
self.NodeChainComboBox.setToolTip("Select the node chain to be used in the node chain operation")
NodeChainCreateButton = QtGui.QPushButton("&Create")
self.connect(NodeChainCreateButton, QtCore.SIGNAL('clicked()'),
self._configure_NodeChain)
NodeChainRefreshButton = QtGui.QPushButton("&Refresh")
self.connect(NodeChainRefreshButton, QtCore.SIGNAL('clicked()'),
self._refresh_node_chains)
hlayoutNodeChain.addWidget(NodeChainLabel)
hlayoutNodeChain.addWidget(self.NodeChainComboBox)
hlayoutNodeChain.addWidget(NodeChainCreateButton)
hlayoutNodeChain.addWidget(NodeChainRefreshButton)
# Create button
operationCreateButton = QtGui.QPushButton("&Create")
self.connect(operationCreateButton, QtCore.SIGNAL('clicked()'),
self._create_operation)
# Main layout
vlayout = QtGui.QVBoxLayout()
vlayout.addLayout(hlayoutData)
vlayout.addLayout(hlayoutNodeChain)
vlayout.addWidget(operationCreateButton)
self.central_widget.setLayout(vlayout)
self.setCentralWidget(self.central_widget)
self.show()
[docs] def _select_input(self):
_input_path = \
str(QtGui.QFileDialog.getExistingDirectory(self,
"Select your input",
pySPACE.configuration.storage))
assert(_input_path.startswith(pySPACE.configuration.storage))
self.selected_input = _input_path[len(pySPACE.configuration.storage)+1:]
self.DataLabel.setText("Input: %s" % self.selected_input)
[docs] def _configure_node_chain(self):
from pySPACE.run.gui.node_chain_GUI import NodeChainConfigurationWidget
NodeChainConfigurationPopup = QtGui.QMainWindow(self)
NodeChainConfigurationWindow = \
NodeChainConfigurationWidget(pySPACE.configuration.spec_dir + os.sep + "node_chains",
NodeChainConfigurationPopup)
NodeChainConfigurationPopup.setCentralWidget(NodeChainConfigurationWindow)
NodeChainConfigurationPopup.setWindowTitle('NodeChain Configuration')
NodeChainConfigurationPopup.show()
[docs] def _refresh_node_chains(self):
self.NodeChainComboBox.clear()
self.NodeChainComboBox.addItems(os.listdir(pySPACE.configuration.spec_dir + os.sep + "node_chains"))
[docs] def _create_operation(self):
operation_spec = {"type": "node_chain",
"input": self.selected_input,
"templates" : [str(self.NodeChainComboBox.currentText())],
"store_node_chain" : False}
self.main_window.operation= create_operation(operation_spec)
self.close()
[docs]class QtStreamHandler(logging.Handler):
""" Handle incoming text streaming input """
[docs] def __init__(self, parent, main):
logging.Handler.__init__(self)
self.parent = parent
self.main = main
self.textWidget = parent
self.formatter = logging.Formatter('%(asctime)s %(name)-20s %(levelname)-8s %(message)s')
self.buffer = ""
self.lastUpdateTime = time.time()
[docs] def emit(self, record):
self.buffer += self.formatter.format(record) + "\n"
if time.time() - self.lastUpdateTime > 0.1:
self.textWidget.insertPlainText(self.buffer)
self.textWidget.moveCursor(QtGui.QTextCursor.End)
self.buffer = ""
self.lastUpdateTime = time.time()
[docs]class LogViewer(QtGui.QWidget):
""" Handle logging input """
[docs] def __init__(self, parent=None):
super(LogViewer, self).__init__(parent)
# Log levels
LEVELS = {'DEBUG': logging.DEBUG,
'INFO': logging.INFO,
'WARNING': logging.WARNING,
'ERROR': logging.ERROR,
'CRITICAL': logging.CRITICAL}
# The actual logger object
self.logger = logging.getLogger('')
# Create log text field
logTextField = QtGui.QTextEdit()
self.logHandler = QtStreamHandler(logTextField, self)
self.logHandler.setLevel(logging.DEBUG)
self.logger.addHandler(self.logHandler)
# Create combobox for selecting the log level
logLevelLabel = QtGui.QLabel("Log level")
logLevelComboBox = QtGui.QComboBox(self)
logLevelComboBox.addItems(["DEBUG", "INFO", "WARNING", 'ERROR', 'CRITICAL'])
def updateLogLevel(level):
self.logHandler.setLevel(LEVELS[str(level)])
self.connect(logLevelComboBox,
QtCore.SIGNAL('activated (const QString&)'),
updateLogLevel)
# Create layout
layout = QtGui.QVBoxLayout()
hlayout = QtGui.QHBoxLayout()
hlayout.addWidget(logLevelLabel)
hlayout.addWidget(logLevelComboBox)
layout.addLayout(hlayout)
layout.addWidget(logTextField)
self.setLayout(layout)