-
Dilawar Singh authored
git-subtree-dir: moose-gui git-subtree-split: a3d3a067901ee0f1f183f50f667a98bebf3e447f
0cda31e2
default.py 58.96 KiB
# default.py ---
#
# Filename: default.py
# Description:
# Author: Subhasis Ray
# Maintainer:
# Created: Tue Nov 13 15:58:31 2012 (+0530)
# Version:
# Last-Updated: Thu Jul 18 10:35:00 2013 (+0530)
# By: subha
# Update #: 2244
# URL:
# Keywords:
# Compatibility:
#
#
# Commentary:
#
# The default placeholder plugin for MOOSE
#
#
# Change log:
#
#
#
#
# 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, 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; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street, Fifth
# Floor, Boston, MA 02110-1301, USA.
#
#
# Code:
import sys
import config
import pickle
import os
from collections import defaultdict
import numpy as np
from PyQt4 import QtGui, QtCore
from PyQt4.Qt import Qt
import moose
from moose import utils
import mtree
from mtoolbutton import MToolButton
from msearch import SearchWidget
from checkcombobox import CheckComboBox
from mplugin import MoosePluginBase, EditorBase, EditorWidgetBase, PlotBase, RunBase
#from defaultToolPanel import DefaultToolPanel
#from DataTable import DataTable
from matplotlib import rcParams
rcParams.update({'figure.autolayout': True})
from matplotlib.lines import Line2D
from PlotWidgetContainer import PlotWidgetContainer
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import QDoubleValidator
from kkitUtil import getColor
from Runner import Runner
# from Runner import Runner
# from __future__ import print_function
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import QToolBar
from PyQt4.QtGui import QToolButton
from PyQt4.QtGui import QLabel
from PyQt4.QtGui import QIcon
from PyQt4.QtGui import QLineEdit
from PyQt4.QtGui import QErrorMessage
from PyQt4.QtGui import QSizeGrip
from PyQt4.QtGui import QIcon
from PyQt4.QtGui import QPixmap
from PyQt4.QtGui import QAction
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
#from EventBlocker import EventBlocker
# from PlotNavigationToolbar import PlotNavigationToolbar
from global_constants import preferences
from setsolver import *
ELECTRICAL_MODEL = 0
CHEMICAL_MODEL = 1
class MoosePlugin(MoosePluginBase):
"""Default plugin for MOOSE GUI"""
def __init__(self, root, mainwindow):
MoosePluginBase.__init__(self, root, mainwindow)
#print "mplugin ",self.getRunView()
#self.connect(self, QtCore.SIGNAL("tableCreated"),self.getRunView().getCentralWidget().plotAllData)
def getPreviousPlugin(self):
return None
def getNextPlugin(self):
return None
def getAdjacentPlugins(self):
return []
def getViews(self):
return self._views
def getCurrentView(self):
return self.currentView
def getEditorView(self):
if not hasattr(self, 'editorView'):
self.editorView = MooseEditorView(self)
#signal to objecteditor from default plugin
self.editorView.getCentralWidget().editObject.connect(self.mainWindow.objectEditSlot)
self.currentView = self.editorView
return self.editorView
def getPlotView(self):
if not hasattr(self, 'plotView'):
self.plotView = PlotView(self)
return self.plotView
def getRunView(self):
if not hasattr(self, 'runView') or self.runView is None:
self.runView = RunView(self.modelRoot, self)
return self.runView
def getMenus(self):
"""Create a custom set of menus."""
return self._menus
class MooseEditorView(EditorBase):
"""Default editor.
"""
def __init__(self, plugin):
EditorBase.__init__(self, plugin)
self.__initMenus()
self.__initToolBars()
def __initMenus(self):
editMenu = QtGui.QMenu('&Edit')
for menu in self.getCentralWidget().getMenus():
editMenu.addMenu(menu)
self._menus.append(editMenu)
def __initToolBars(self):
for toolbar in self.getCentralWidget().getToolBars():
self._toolBars.append(toolbar)
def getToolPanes(self):
return super(MooseEditorView, self).getToolPanes()
def getLibraryPane(self):
return super(MooseEditorView, self).getLibraryPane()
def getOperationsWidget(self):
return super(MooseEditorView, self).getOperationsPane()
def getCentralWidget(self):
"""Retrieve or initialize the central widget.
Note that we call the widget's setModelRoot() function
explicitly with the plugin's modelRoot as the argument. This
enforces an update of the widget display with the current
modelRoot.
This function should be overridden by any derived class as it
has the editor widget class hard coded into it.
"""
if self._centralWidget is None:
self._centralWidget = DefaultEditorWidget()
if hasattr(self._centralWidget, 'init'):
self._centralWidget.init()
self._centralWidget.setModelRoot(self.plugin.modelRoot)
return self._centralWidget
class MooseTreeEditor(mtree.MooseTreeWidget):
"""Subclass of MooseTreeWidget to implement drag and drop events. It
creates an element under the drop location using the dropped mime
data as class name.
"""
def __init__(self, *args):
mtree.MooseTreeWidget.__init__(self, *args)
def dragEnterEvent(self, event):
if event.mimeData().hasFormat('text/plain'):
event.acceptProposedAction()
def dragMoveEvent(self, event):
if event.mimeData().hasFormat('text/plain'):
event.acceptProposedAction()
def dropEvent(self, event):
"""Insert an element of the specified class in drop location"""
if not event.mimeData().hasFormat('text/plain'):
return
pos = event.pos()
item = self.itemAt(pos)
try:
self.insertChildElement(item, str(event.mimeData().text()))
event.acceptProposedAction()
except NameError:
return
class DefaultEditorWidget(EditorWidgetBase):
"""Editor widget for default plugin.
Plugin-writers should code there own editor widgets derived from
EditorWidgetBase.
It adds a toolbar for inserting moose objects into the element
tree. The toolbar contains MToolButtons for moose classes.
Signals: editObject - inherited from EditorWidgetBase , emitted
with currently selected element's path as argument. Should be
connected to whatever slot is responsible for firing the object
editor in top level.
"""
def __init__(self, *args):
EditorWidgetBase.__init__(self, *args)
layout = QtGui.QHBoxLayout()
self.setLayout(layout)
self.tree = MooseTreeEditor()
self.tree.setAcceptDrops(True)
self.getTreeMenu()
self.layout().addWidget(self.tree)
def getTreeMenu(self):
try:
return self.treeMenu
except AttributeError:
self.treeMenu = QtGui.QMenu()
self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
self.tree.customContextMenuRequested.connect(lambda : self.treeMenu.exec_(QtGui.QCursor.pos()) )
# Inserting a child element
self.insertMenu = QtGui.QMenu('Insert')
self._menus.append(self.insertMenu)
self.treeMenu.addMenu(self.insertMenu)
self.insertMapper = QtCore.QSignalMapper(self)
ignored_bases = ['ZPool', 'Msg', 'Panel', 'SolverBase', 'none']
ignored_classes = ['ZPool','ZReac','ZMMenz','ZEnz','CplxEnzBase']
classlist = [ch[0].name for ch in moose.element('/classes').children
if (ch[0].baseClass not in ignored_bases)
and (ch[0].name not in (ignored_bases + ignored_classes))
and not ch[0].name.startswith('Zombie')
and not ch[0].name.endswith('Base')
]
insertMapper, actions = self.getInsertActions(classlist)
for action in actions:
self.insertMenu.addAction(action)
self.connect(insertMapper, QtCore.SIGNAL('mapped(const QString&)'), self.tree.insertElementSlot)
self.editAction = QtGui.QAction('Edit', self.treeMenu)
self.editAction.triggered.connect(self.editCurrentObjectSlot)
self.tree.elementInserted.connect(self.elementInsertedSlot)
self.treeMenu.addAction(self.editAction)
return self.treeMenu
def updateModelView(self):
self.tree.recreateTree(root=self.modelRoot)
# if current in self.tree.odict:
# self.tree.setCurrentItem(current)
def updateItemSlot(self, mobj):
"""This should be overridden by derived classes to connect appropriate
slot for updating the display item.
"""
self.tree.updateItemSlot(mobj)
def editCurrentObjectSlot(self):
"""Emits an `editObject(str)` signal with moose element path of
currently selected tree item as argument
"""
mobj = self.tree.currentItem().mobj
self.editObject.emit(mobj.path)
def sizeHint(self):
return QtCore.QSize(400, 300)
def getToolBars(self):
if not hasattr(self, '_insertToolBar'):
self._insertToolBar = QtGui.QToolBar('Insert')
return self._toolBars
for action in self.insertMenu.actions():
button = MToolButton()
button.setDefaultAction(action)
self._insertToolBar.addWidget(button)
self._toolBars.append(self._insertToolBar)
return self._toolBars
############################################################
#
# View for running a simulation and runtime visualization
#
############################################################
from mplot import CanvasWidget
class RunView(RunBase):
"""A default runtime view implementation. This should be
sufficient for most common usage.
canvas: widget for plotting
dataRoot: location of data tables
"""
def __init__(self, modelRoot, *args, **kwargs):
RunBase.__init__(self, *args, **kwargs)
self.modelRoot = modelRoot
if modelRoot != "/":
self.dataRoot = modelRoot + '/data'
else:
self.dataRoot = "/data"
self.setModelRoot(moose.Neutral(self.plugin.modelRoot).path)
self.setDataRoot(moose.Neutral('/data').path)
self.setDataRoot(moose.Neutral(self.plugin.modelRoot).path)
self.plugin.modelRootChanged.connect(self.setModelRoot)
self.plugin.dataRootChanged.connect(self.setDataRoot)
# self.getCentralWidget()
self._menus += self.getCentralWidget().getMenus()
def getCentralWidget(self):
"""TODO: replace this with an option for multiple canvas
tabs"""
if self._centralWidget is None:
self._centralWidget = PlotWidgetContainer(self.modelRoot)
return self._centralWidget
def setDataRoot(self, path):
self.dataRoot = path
def setModelRoot(self, path):
self.modelRoot = path
def getDataTablesPane(self):
"""This should create a tree widget with dataRoot as the root
to allow visual selection of data tables for plotting."""
raise NotImplementedError()
def plotAllData(self):
"""This is wrapper over the same function in PlotWidget."""
self.centralWidget.plotAllData()
def getToolPanes(self):
return []
if not self._toolPanes:
self._toolPanes = [self.getSchedulingDockWidget()]
return self._toolPanes
def getSchedulingDockWidget(self):
"""Create and/or return a widget for schduling"""
if hasattr(self, 'schedulingDockWidget') and self.schedulingDockWidget is not None:
return self.schedulingDockWidget
self.schedulingDockWidget = QtGui.QDockWidget('Scheduling')
self.schedulingDockWidget.setFeatures( QtGui.QDockWidget.NoDockWidgetFeatures);
self.schedulingDockWidget.setWindowFlags(Qt.CustomizeWindowHint)
titleWidget = QtGui.QWidget();
self.schedulingDockWidget.setTitleBarWidget(titleWidget)
widget = SchedulingWidget()
widget.setDataRoot(self.dataRoot)
widget.setModelRoot(self.modelRoot)
self.schedulingDockWidget.setWidget(widget)
widget.runner.simulationStarted.connect(self._centralWidget.extendXAxes)
widget.runner.simulationProgressed.connect(self._centralWidget.updatePlots)
widget.runner.simulationFinished.connect(self._centralWidget.rescalePlots)
# widget.runner.simulationContinued.connect(self._centralWidget.extendXAxes)
widget.runner.simulationReset.connect(self._centralWidget.plotAllData)
self._toolBars += widget.getToolBars()
return self.schedulingDockWidget
'''
class MooseRunner(QtCore.QObject):
"""Helper class to control simulation execution
See: http://doc.qt.digia.com/qq/qq27-responsive-guis.html :
'Solving a Problem Step by Step' for design details.
"""
resetAndRun = QtCore.pyqtSignal(name='resetAndRun')
update = QtCore.pyqtSignal(name='update')
currentTime = QtCore.pyqtSignal(float, name='currentTime')
finished = QtCore.pyqtSignal(name='finished')
def __init__( self
, runTime
, updateInterval
):
QtCore.QObject.__init__(self)
# if (MooseRunner.inited):
# return
self.runTime = runTime
self.updateInterval = updateInterval
self._updateInterval = 100e-3
self._simtime = 0.0
self._clock = moose.Clock('/clock')
self._pause = False
self.dataRoot = '/data'
self.modelRoot = '/model'
#MooseRunner.inited = True
def doResetAndRun(self, tickDtMap, tickTargetMap, simtime, updateInterval):
self._pause = False
self._updateInterval = 0.1 #updateInterval
self._simtime = simtime
utils.updateTicks(tickDtMap)
utils.assignTicks(tickTargetMap)
self.resetAndRun.emit()
moose.reinit()
QtCore.QTimer.singleShot(0, self.run)
def run(self):
"""Run simulation for a small interval."""
print("simtime => ", self._simtime)
print("update interval => ", self._updateInterval)
print("current time => ", self._clock.currentTime)
print("Base dt => ", self._clock.baseDt)
if self._clock.currentTime >= self._simtime:
self.finished.emit()
return
if self._pause:
return
toRun = self._simtime - self._clock.currentTime
if toRun > self._updateInterval:
toRun = self._updateInterval
if toRun < self._clock.baseDt:
return
moose.start(toRun)
self.update.emit()
self.currentTime.emit(self._clock.currentTime)
QtCore.QTimer.singleShot(0, self.run)
def continueRun(self, simtime, updateInterval):
"""Continue running without reset for `simtime`."""
self._simtime = simtime
self._updateInterval = updateInterval
self._pause = False
QtCore.QTimer.singleShot(0, self.run)
def stop(self):
"""Pause simulation"""
self._pause = True
'''
class SchedulingWidget(QtGui.QWidget):
"""Widget for scheduling.
Important member fields:
runner - object to run/pause/continue simulation. Whenever
`updateInterval` time has been simulated this object sends an
`update()` signal. This can be connected to other objects to
update their data.
SIGNALS:
resetAndRun(tickDt, tickTargets, simtime, updateInterval)
tickDt: dict mapping tick nos to dt
tickTargets: dict mapping ticks to target paths
simtime: total simulation runtime
updateInterval: interval between update signals are to be emitted.
simtimeExtended(simtime)
emitted when simulation time is increased by user.
"""
resetAndRun = QtCore.pyqtSignal(dict, dict, float, float, name='resetAndRun')
simtimeExtended = QtCore.pyqtSignal(float, name='simtimeExtended')
continueRun = QtCore.pyqtSignal(float, float, name='continueRun')
def __init__(self, *args, **kwargs):
QtGui.QWidget.__init__(self, *args, **kwargs)
self.simulationInterval = None
self.updateInterval = None
self.runTime = None
# if not self.advanceOptiondisplayed:
# self.advancedOptionsWidget.hide()
# self.__getUpdateIntervalWidget()
#layout.addWidget(self.__getUpdateIntervalWidget())
# spacerItem = QtGui.QSpacerItem(450, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
# layout.addItem(spacerItem)
# self.setLayout(layout)
# self._toolBars.append(
self.modelRoot = None
self.dataRoot = None
self.runner = Runner()
self.resetAndRunAction = None
self.stopAction = None
self.continueAction = None
self.preferences = preferences
self.currentSimulationRuntime = None
self.modelType = None
self.simulationRuntime = None
self.schedulerToolBar = self.getSchedulerToolBar()
self.runner.simulationProgressed.connect(self.updateCurrentSimulationRuntime)
self.continueFlag = False
self.preferences.applyChemicalSettings.connect(self.resetSimulation)
# self.resetAndRunButton.clicked.connect(self.resetAndRunSlot)
# self.continueButton.clicked.connect(self.doContinueRun)
# self.continueRun.connect(self.runner.continueRun)
# self.stopButton.clicked.connect(self.runner.stop)
def updateCurrentSimulationRuntime(self, time):
self.currentSimulationRuntime.setText(str(time))
def getToolBars(self):
return [self.schedulerToolBar]
def getSchedulerToolBar(self):
bar = QToolBar("Run", self)
self.resetAction = bar.addAction( QIcon('icons/arrow_undo.png')
, 'Reset'
, self.resetSimulation
)
self.resetAction.setToolTip('Reset simulation.')
self.runAction = bar.addAction( QIcon('icons/run.png')
, 'Run'
, self.runSimulation
)
self.runAction.setToolTip('Run simulation.')
self.stopAction = bar.addAction( QIcon('icons/stop.png')
, 'Stop'
, self.runner.togglePauseSimulation
)
self.stopAction.setToolTip('Stop simulation.')
bar.addSeparator()
runtimeLabel = QLabel('Run for')
self.simulationRuntime = QLineEdit()
self.simulationRuntime.setValidator(QDoubleValidator())
self.simulationRuntime.setFixedWidth(75)
bar.addWidget(runtimeLabel)
bar.addWidget(self.simulationRuntime)
bar.addWidget(QLabel(' (s)'))
bar.addSeparator()
#: current time
# spacer.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Preferred)
# self._runToolBar.addWidget(spacer)
# self._runToolBar.addWidget(QtGui.QLabel('Current time'))
self.currentSimulationRuntime = QLineEdit() # 6 digits
self.currentSimulationRuntime.setToolTip('Current simulation runtime.')
self.currentSimulationRuntime.setFixedWidth(75)
self.currentSimulationRuntime.setValidator(QDoubleValidator())
self.currentSimulationRuntime.setText("0.0")
self.currentSimulationRuntime.setReadOnly(True)
# self.runner.currentTime.connect(self.currentTimeWidget.display)
bar.addWidget(QLabel("Current Time : "))
bar.addWidget(self.currentSimulationRuntime)
bar.addWidget(QLabel(" (s)"))
# self._runToolBar.addWidget(self.())
bar.addSeparator()
self.preferencesButton = QToolButton()
self.preferencesButton.setText("Preferences")
self.preferencesButton.clicked.connect(self.preferencesToggler)
bar.addWidget(self.preferencesButton)
return bar
# def updateTickswidget(self):
# if self.advanceOptiondisplayed:
# self.advancedOptionsWidget.hide()
# self.advanceOptiondisplayed = False
# else:
# self.advancedOptionsWidget.show()
# self.advanceOptiondisplayed = True
def continueSimulation(self):
self.runner.continueSimulation( self.runTime
, self.updateInterval
, self.simulationInterval
)
self.simulationRuntime.setText(str(float(self.simulationRuntime.text()) + self.runTime))
def resetSimulation(self):
self.setParameters()
try:
self.runtime = float(runtime)
except:
self.runtime = 100.0
# print(self.runTime)
# print(self.updateInterval)
# print(self.simulationInterval)
self.currentSimulationRuntime.setText("0.0")
self.checkConsistency()
# self.preferences.setChemicalClocks()
self.simulationRuntime.setText(str(self.runTime))
self.runner.resetSimulation( self.runTime
, self.updateInterval
, self.simulationInterval
)
self.continueFlag = False
def runSimulation(self):
if self.modelType == CHEMICAL_MODEL:
compt = moose.wildcardFind(self.modelRoot+'/##[ISA=ChemCompt]')
if not moose.exists(compt[0].path+'/stoich'):
chemPref = self.preferences.getChemicalPreferences()
solver = chemPref["simulation"]["solver"]
addSolver(self.modelRoot,solver)
status = self.solverStatus()
#print "status ",status
# if status != 0 or status == -1:
# return
if status == None or int(status) == -1 or int(status) == 0:
#allow the model to Run
pass
else:
# if something is dangling or solver is not set then return
return
runtime = str(self.simulationRuntime.text())
try:
self.runtime = float(runtime)
except:
self.runtime = 100.0
self.simulationRuntime.setText("100.0")
self.checkConsistency()
self.continueSimulation = True
self.runner.runSimulation(self.runtime)
# return
# if self.continueFlag:
# self.continueSimulation()
# else:
# self.runner.runSimulation()
# self.continueFlag = True
def setParameters(self):
if self.modelType == ELECTRICAL_MODEL:
self.setElectricalParameters()
elif self.modelType == CHEMICAL_MODEL:
self.setChemicalParameters()
def setChemicalParameters(self):
chemicalPreferences = self.preferences.getChemicalPreferences()
self.preferences.initializeChemicalClocks()
self.updateInterval = chemicalPreferences["simulation"]["gui-update-interval"]
self.simulationInterval = chemicalPreferences["simulation"]["simulation-dt"]
if str(self.simulationRuntime.text()) == "":
self.simulationRuntime.setText(str(chemicalPreferences["simulation"]["default-runtime"]))
self.runTime = float(self.simulationRuntime.text())
self.solver = chemicalPreferences["simulation"]["solver"]
#print(self.solver)
def setElectricalParameters(self):
electricalPreferences = self.preferences.getElectricalPreferences()
self.preferences.initializeElectricalClocks()
self.updateInterval = electricalPreferences["simulation"]["gui-update-interval"]
self.simulationInterval = electricalPreferences["simulation"]["simulation-dt"]
if str(self.simulationRuntime.text()) == "":
self.simulationRuntime.setText(str(electricalPreferences["simulation"]["default-runtime"]))
self.runTime = float(self.simulationRuntime.text())
self.solver = electricalPreferences["simulation"]["solver"]
#print(self.solver)
def checkConsistency(self):
if self.updateInterval < self.simulationInterval :
self.updateInterval = self.simulationInterval
# print("Hello")
# dialog = QErrorMessage()
# dialog.showMessage(
# """GUI Update interval should be greater than Simulation Interval.
# Please update these values in Edit > Preferences."""
# )
# return False
if self.runTime < self.updateInterval :
self.runTime = self.updateInterval
# dialog = QErrorMessage()
# dialog.showMessage(
# """Simulation runtime should greater than GUI Update interval.
# Please update the runtime in the Scheduling Toolbar"""
# )
# return False
return True
def solverStatus(self):
compt = moose.wildcardFind(self.modelRoot+'/##[ISA=ChemCompt]')
if not moose.exists(compt[0].path+'/stoich'):
return None
else:
stoich = moose.Stoich(compt[0].path+'/stoich')
status = int(stoich.status)
# print("Status =>", status)
if status == -1:
QtGui.QMessageBox.warning(None,"Could not Run the model","Warning: Reaction path not yet assigned.\n ")
return -1
if status == 1:
QtGui.QMessageBox.warning(None,"Could not Run the model","Warning: Missing a reactant in a Reac or Enz.\n ")
return 1
elif status == 2:
QtGui.QMessageBox.warning(None,"Could not Run the model","Warning: Missing a substrate in an MMenz.\n ")
return 2
elif status == 3:
QtGui.QMessageBox.warning(None,"Could not Run the model","Warning: Missing substrates as well as reactants.\n ")
return 3
elif status == 4:
QtGui.QMessageBox.warning(None,"Could not Run the model"," Warning: Compartment not defined.\n ")
return 4
elif status == 8:
QtGui.QMessageBox.warning(None,"Could not Run the model","Warning: Neither Ksolve nor Dsolve defined.\n ")
return 8
elif status == 16:
QtGui.QMessageBox.warning(None,"Could not Run the model","Warning: No objects found on path.\n ")
return 16
elif status == 0:
print "Successfully built stoichiometry matrix.\n "
# moose.reinit()
return 0
# def setElectricalParameters(self):
# chemicalPreferences = self.preferences.getChemicalPreferences()
# self.updateInterval = chemicalPreferences["guiUpdateInterval"]
# self.simulationInterval = chemicalPreferences["simulationInterval"]
# chemicalPreferences["diffusionInterval"]
# chemicalPreferences
# self. chemicalPreferences
# self. chemicalPreferences
# self. chemicalPreferences
# self. runTime = float(self.simulationRuntime.text())
def __getAdvanceOptionsButton(self):
icon = QtGui.QIcon(os.path.join(config.settings[config.KEY_ICON_DIR],'arrow.png'))
# self.advancedOptionsButton.setIcon(QtGui.QIcon(icon))
# self.advancedOptionsButton.setToolButtonStyle( Qt.ToolButtonTextBesideIcon );
return self.advancedOptionsButton
def preferencesToggler(self):
visibility = not self.preferences.getView().isVisible()
self.preferences.getView().setVisible(visibility)
def continueSlot(self):
pass
def updateCurrentTime(self):
sys.stdout.flush()
self.currentTimeWidget.dispay(str(moose.Clock('/clock').currentTime))
def updateTextFromTick(self, tickNo):
tick = moose.vector('/clock/tick')[tickNo]
widget = self.tickListWidget.layout().itemAtPosition(tickNo + 1, 1).widget()
if widget is not None and isinstance(widget, QtGui.QLineEdit):
widget.setText(str(tick.dt))
def updateFromMoose(self):
"""Update the tick dt from the tick objects"""
ticks = moose.vector('/clock/tick')
# Items at position 0 are the column headers, hence ii+1
for ii in range(ticks[0].localNumField):
self.updateTextFromTick(ii)
self.updateCurrentTime()
def getSimTime(self):
try:
time = float(str(self.simtimeEdit.text()))
return time
except ValueError, e:
QtGui.QMessageBox.warning(self, 'Invalid value', 'Specified runtime was meaningless.')
return 0
def setDataRoot(self, root='/data'):
self.dataRoot = moose.element(root).path
def setModelRoot(self, root='/model'):
self.modelRoot = moose.element(root).path
self.setModelType()
def setModelType(self):
if moose.exists(self.modelRoot + "/model/cells"):
self.modelType = ELECTRICAL_MODEL
else:
self.modelType = CHEMICAL_MODEL
self.resetSimulation()
from collections import namedtuple
# Keeps track of data sources for a plot. 'x' can be a table path or
# '/clock' to indicate time points from moose simulations (which will
# be created from currentTime field of the `/clock` element and the
# number of dat points in 'y'. 'y' should be a table. 'z' can be empty
# string or another table or something else. Will not be used most of
# the time (unless 3D or heatmap plotting).
PlotDataSource = namedtuple('PlotDataSource', ['x', 'y', 'z'], verbose=False)
event = None
legend = None
canvas = None
from PyQt4.QtGui import QWidget
from PyQt4.QtGui import QSizeGrip
from PyQt4.QtGui import QLayout
from PyQt4.QtGui import QScrollArea
from PyQt4.QtGui import QMenu
from PyQt4.QtCore import pyqtSlot,SIGNAL,SLOT, Signal, pyqtSignal
class PlotWidget(QWidget):
"""A wrapper over CanvasWidget to handle additional MOOSE-specific
stuff.
modelRoot - path to the entire model our plugin is handling
dataRoot - path to the container of data tables.
TODO: do we really need this separation or should we go for
standardizing location of data with respect to model root.
pathToLine - map from moose path to Line2D objects in plot. Can
one moose table be plotted multiple times? Maybe yes (e.g., when
you want multiple other tables to be compared with the same data).
lineToDataSource - map from Line2D objects to moose paths
"""
widgetClosedSignal = pyqtSignal(object)
addGraph = pyqtSignal(object)
def __init__(self, model, graph, index, parentWidget, *args, **kwargs):
super(PlotWidget, self).__init__()
self.model = model
self.graph = graph
self.index = index
self.menu = self.getContextMenu()
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.connect( self
, SIGNAL("customContextMenuRequested(QPoint)")
, self
, SLOT("contextMenuRequested(QPoint)")
)
self.canvas = CanvasWidget(self.model, self.graph, self.index)
self.canvas.setParent(self)
self.navToolbar = NavigationToolbar(self.canvas, self)
self.hackNavigationToolbar()
self.canvas.mpl_connect('pick_event',self.togglePlot)
# self.canvas.
# self.navToolbar.addSeparator()
layout = QtGui.QGridLayout()
# canvasScrollArea = QScrollArea()
# canvasScrollArea.setWidget(self.canvas)
layout.addWidget(self.navToolbar, 0, 0)
layout.addWidget(self.canvas, 1, 0)
self.setLayout(layout)
# self.setAcceptDrops(True)
#self.modelRoot = '/'
self.pathToLine = defaultdict(set)
self.lineToDataSource = {}
self.axesRef = self.canvas.addSubplot(1, 1)
# box = self.axesRef.get_position()
# self.axesRef.set_position([box.x0, box.y0, box.width * 0.8, box.height])
self.legend = None
# layout.setSizeConstraint( QLayout.SetNoConstraint )
# self.setSizePolicy( QtGui.QSizePolicy.Expanding
# , QtGui.QSizePolicy.Expanding
# )
desktop = QtGui.QApplication.desktop()
# print("**********************")
# print(desktop.screenGeometry())
# print("***********************")
self.setMinimumSize(desktop.screenGeometry().width() / 4, desktop.screenGeometry().height() / 3)
# self.setMaximumSize(desktop.screenGeometry().width() / 2, desktop.screenGeometry().height() / 2)
# self.setMinimumSize(self.width(), self.height())
# self.setMaximumSize(2 * self.width(), 2* self.height())
# QtCore.QObject.connect(utils.tableEmitter,QtCore.SIGNAL("tableCreated()"),self.plotAllData)
self.canvas.updateSignal.connect(self.plotAllData)
self.plotAllData()
# self.plotView = PlotView(model, graph, index, self)
#self.dataTable = DataTable()
#utils.tableCreated.connect(plotAllData)
# self.plotAllData()
# self.setSizePolicy(QtGui.QSizePolicy.Fixed,
# QtGui.QSizePolicy.Expanding)
def hackNavigationToolbar(self):
# ADD Graph Action
pixmap = QPixmap("icons/add_graph.png")
icon = QIcon(pixmap)
action = QAction(icon, "Add a graph", self.navToolbar)
# self.navToolbar.addAction(action)
action.triggered.connect(self.addGraph.emit)
self.navToolbar.insertAction(self.navToolbar.actions()[0], action)
# Delete Graph Action
pixmap = QPixmap("icons/delete_graph.png")
icon = QIcon(pixmap)
action = QAction(icon, "Delete this graph", self.navToolbar)
# self.navToolbar.addAction(action)
action.triggered.connect(self.delete)
self.navToolbar.insertAction(self.navToolbar.actions()[1], action)
#Toggle Grid Action
pixmap = QPixmap("icons/grid.png")
icon = QIcon(pixmap)
action = QAction(icon, "Toggle Grid", self.navToolbar)
# self.navToolbar.addAction(action)
action.triggered.connect(self.canvas.toggleGrid)
self.navToolbar.insertAction(self.navToolbar.actions()[2], action)
self.navToolbar.insertSeparator(self.navToolbar.actions()[3])
@property
def plotAll(self):
return len(self.pathToLine) == 0
def toggleLegend(self):
if self.legend is not None:
self.legend.set_visible(not self.legend.get_visible())
self.canvas.draw()
def getContextMenu(self):
menu = QMenu()
# closeAction = menu.addAction("Delete")
exportCsvAction = menu.addAction("Export to CSV")
exportCsvAction.triggered.connect(self.saveAllCsv)
toggleLegendAction = menu.addAction("Toggle legend")
toggleLegendAction.triggered.connect(self.toggleLegend)
self.removeSubmenu = menu.addMenu("Remove")
# configureAction.triggered.connect(self.configure)
# self.connect(,SIGNAL("triggered()"),
# self,SLOT("slotShow500x500()"))
# self.connect(action1,SIGNAL("triggered()"),
# self,SLOT("slotShow100x100()"))
return menu
def deleteGraph(self):
print("Deleting " + self.graph.path)
moose.delete(self.graph.path)
def delete(self, event):
print("Deleting PlotWidget")
self.deleteGraph()
self.close()
self.widgetClosedSignal.emit(self)
def configure(self, event):
print("Displaying configure view!")
self.plotView.getCentralWidget().show()
@pyqtSlot(QtCore.QPoint)
def contextMenuRequested(self,point):
# menu = QtGui.QMenu()
# # action1 = menu.addAction("Set Size 100x100")
# # action2 = menu.addAction("Set Size 500x500")
# # self.connect(action2,SIGNAL("triggered()"),
# # self,SLOT("slotShow500x500()"))
# # self.connect(action1,SIGNAL("triggered()"),
# # self,SLOT("slotShow100x100()"))
self.menu.exec_(self.mapToGlobal(point))
def setModelRoot(self, path):
self.modelRoot = path
def setDataRoot(self, path):
self.dataRoot = path
#plotAllData()
def genColorMap(self,tableObject):
#print "tableObject in colorMap ",tableObject
species = tableObject+'/info'
colormap_file = open(os.path.join(config.settings[config.KEY_COLORMAP_DIR], 'rainbow2.pkl'),'rb')
self.colorMap = pickle.load(colormap_file)
colormap_file.close()
hexchars = "0123456789ABCDEF"
color = 'white'
#Genesis model exist the path and color will be set but not xml file so bypassing
#print "here genColorMap ",moose.exists(species)
if moose.exists(species):
color = moose.element(species).getField('color')
if ((not isinstance(color,(list,tuple)))):
if color.isdigit():
tc = int(color)
tc = (tc * 2 )
r,g,b = self.colorMap[tc]
color = "#"+ hexchars[r / 16] + hexchars[r % 16] + hexchars[g / 16] + hexchars[g % 16] + hexchars[b / 16] + hexchars[b % 16]
else:
color = 'white'
return color
def removePlot(self, table):
print("removePlot =>", table)
moose.delete(table)
self.plotAllData()
def makeRemovePlotAction(self, label, table):
action = self.removeSubmenu.addAction(label)
action.triggered.connect(lambda: self.removePlot(table))
return action
def plotAllData(self):
"""Plot data from existing tables"""
self.axesRef.lines = []
self.pathToLine.clear()
self.removeSubmenu.clear()
if self.legend is not None:
self.legend.set_visible(False)
path = self.model.path
modelroot = self.model.path
time = moose.Clock('/clock').currentTime
tabList = []
#for tabId in moose.wildcardFind('%s/##[TYPE=Table]' % (path)):
#harsha: policy graphs will be under /model/modelName need to change in kkit
#for tabId in moose.wildcardFind('%s/##[TYPE=Table]' % (modelroot)):
plotTables = list(moose.wildcardFind(self.graph.path + '/##[TYPE=Table]'))
plotTables.extend(moose.wildcardFind(self.graph.path + '/##[TYPE=Table2]'))
if len (plotTables) > 0:
for tabId in plotTables:
tab = moose.Table(tabId)
#print("Table =>", tab)
line_list=[]
tableObject = tab.neighbors['requestOut']
# Not a good way
#tableObject.msgOut[0]
if len(tableObject) > 0:
# This is the default case: we do not plot the same
# table twice. But in special cases we want to have
# multiple variations of the same table on different
# axes.
#
#Harsha: Adding color to graph for signalling model, check if given path has cubemesh or cylmesh
color = '#FFFFFF'
if moose.exists(tableObject[0].path + '/info'):
color = getColor(tableObject[0].path + '/info')
color = str(color[1].name()).upper()
lines = self.pathToLine[tab.path]
if len(lines) == 0:
#Harsha: pass color for plot if exist and not white else random color
#print "tab in plotAllData ",tab, tab.path,tab.name
field = tab.path.rpartition(".")[-1]
if field.endswith("[0]") or field.endswith("_0_"):
field = field[:-3]
# label = ( tableObject[0].path.partition(self.model.path + "/model[0]/")[-1]
# + "."
# + field
# )
label = ( tableObject[0].path.rpartition("/")[-1]
+ "."
+ field
)
self.makeRemovePlotAction(label, tab)
if (color != '#FFFFFF'):
newLines = self.addTimeSeries(tab, label=label,color=color)
else:
newLines = self.addTimeSeries(tab, label=label)
self.pathToLine[tab.path].update(newLines)
for line in newLines:
self.lineToDataSource[line] = PlotDataSource(x='/clock', y=tab.path, z='')
else:
for line in lines:
dataSrc = self.lineToDataSource[line]
xSrc = moose.element(dataSrc.x)
ySrc = moose.element(dataSrc.y)
if isinstance(xSrc, moose.Clock):
ts = np.linspace(0, time, len(tab.vector))
elif isinstance(xSrc, moose.Table):
ts = xSrc.vector.copy()
line.set_data(ts, tab.vector.copy())
tabList.append(tab)
# if len(tabList) > 0:
self.legend = self.canvas.callAxesFn( 'legend'
, loc='upper right'
, prop= {'size' : 10 }
# , bbox_to_anchor=(1.0, 0.5)
, fancybox = True
, shadow=False
, ncol=1
)
if self.legend is not None:
self.legend.draggable()
self.legend.get_frame().set_alpha(0.5)
self.legend.set_visible(True)
self.canvas.draw()
# # leg = self.canvas.callAxesFn( 'legend'
# # , loc ='upper right'
# # , prop = {'size' : 10 }
# # # , bbox_to_anchor = (0.5, -0.03)
# # , fancybox = False
# # # , shadow = True
# # , ncol = 1
# # )
# # leg.draggable(False)
# # print(leg.get_window_extent())
# #leg = self.canvas.callAxesFn('legend')
# #leg = self.canvas.callAxesFn('legend',loc='upper left', fancybox=True, shadow=True)
# #global legend
# #legend =leg
# for legobj in leg.legendHandles:
# legobj.set_linewidth(5.0)
# legobj.set_picker(True)
# else:
# print "returning as len tabId is zero ",tabId, " tableObject ",tableObject, " len ",len(tableObject)
def togglePlot(self, event):
#print "onclick",event1.artist.get_label()
#harsha:To workout with double-event-registered on onclick event
#http://stackoverflow.com/questions/16278358/double-event-registered-on-mouse-click-if-legend-is-outside-axes
legline = event.artist
for line in self.axesRef.lines:
if line.get_label() == event.artist.get_label():
vis = not line.get_visible()
line.set_visible(vis)
if vis:
legline.set_alpha(1.0)
else:
legline.set_alpha(0.2)
break
self.canvas.draw()
def addTimeSeries(self, table, *args, **kwargs):
ts = np.linspace(0, moose.Clock('/clock').currentTime, len(table.vector))
return self.canvas.plot(ts, table.vector, *args, **kwargs)
def addRasterPlot(self, eventtable, yoffset=0, *args, **kwargs):
"""Add raster plot of events in eventtable.
yoffset - offset along Y-axis.
"""
y = np.ones(len(eventtable.vector)) * yoffset
return self.canvas.plot(eventtable.vector, y, '|')
def updatePlots(self):
for path, lines in self.pathToLine.items():
element = moose.element(path)
if isinstance(element, moose.Table2):
tab = moose.Table2(path)
else:
tab = moose.Table(path)
data = tab.vector
ts = np.linspace(0, moose.Clock('/clock').currentTime, len(data))
for line in lines:
line.set_data(ts, data)
self.canvas.draw()
def extendXAxes(self, xlim):
for axes in self.canvas.axes.values():
# axes.autoscale(False, axis='x', tight=True)
axes.set_xlim(right=xlim)
axes.autoscale_view(tight=True, scalex=True, scaley=True)
self.canvas.draw()
def rescalePlots(self):
"""This is to rescale plots at the end of simulation.
ideally we should set xlim from simtime.
"""
for axes in self.canvas.axes.values():
axes.autoscale(True, tight=True)
axes.relim()
axes.autoscale_view(tight=True,scalex=True,scaley=True)
self.canvas.draw()
def saveCsv(self, line, directory):
"""Save selected plot data in CSV file"""
src = self.lineToDataSource[line]
xSrc = moose.element(src.x)
ySrc = moose.element(src.y)
y = ySrc.vector.copy()
if isinstance(xSrc, moose.Clock):
x = np.linspace(0, xSrc.currentTime, len(y))
elif isinstance(xSrc, moose.Table):
x = xSrc.vector.copy()
filename = str(directory)+'/'+'%s.csv' % (ySrc.name)
np.savetxt(filename, np.vstack((x, y)).transpose())
print 'Saved data from %s and %s in %s' % (xSrc.path, ySrc.path, filename)
def saveAllCsv(self):
"""Save data for all currently plotted lines"""
#Harsha: Plots were saved in GUI folder instead provided QFileDialog box to save to
#user choose
fileDialog2 = QtGui.QFileDialog(self)
fileDialog2.setFileMode(QtGui.QFileDialog.Directory)
fileDialog2.setWindowTitle('Select Directory to save plots')
fileDialog2.setOptions(QtGui.QFileDialog.ShowDirsOnly)
fileDialog2.setLabelText(QtGui.QFileDialog.Accept, self.tr("Save"))
targetPanel = QtGui.QFrame(fileDialog2)
targetPanel.setLayout(QtGui.QVBoxLayout())
layout = fileDialog2.layout()
layout.addWidget(targetPanel)
if fileDialog2.exec_():
directory = fileDialog2.directory().path()
for line in self.lineToDataSource.keys():
self.saveCsv(line,directory)
def getMenus(self):
if not hasattr(self, '_menus'):
self._menus = []
self.plotAllAction = QtGui.QAction('Plot all data', self)
self.plotAllAction.triggered.connect(self.plotAllData)
self.plotMenu = QtGui.QMenu('Plot')
self.plotMenu.addAction(self.plotAllAction)
self.saveAllCsvAction = QtGui.QAction('Save all data in CSV files', self)
self.saveAllCsvAction.triggered.connect(self.saveAllCsv)
self.plotMenu.addAction(self.saveAllCsvAction)
self._menus.append(self.plotMenu)
return self._menus
# def resizeEvent(self, event):
# print("Here", event)
# self.canvas.figure.subplots_adjust(bottom=0.2)#, left = 0.18)
###################################################
#
# Plot view - select fields to record
#
###################################################
'''
class PlotView(PlotBase):
"""View for selecting fields on elements to plot."""
def __init__(self, model, graph, index, *args):
PlotBase.__init__(self, *args)
self.model = model
self.graph = graph
self.index = index
# self.plugin.modelRootChanged.connect(self.getSelectionPane().setSearchRoot)
# self.plugin.dataRootChanged.connect(self.setDataRoot)
# self.dataRoot = self.plugin.dataRoot
def setDataRoot(self, root):
self.dataRoot = moose.element(root).path
def getToolPanes(self):
return (self.getFieldSelectionDock(), )
def getSelectionPane(self):
"""Creates a widget to select elements and fields for plotting.
search-root, field-name, comparison operator , value
"""
if not hasattr(self, '_selectionPane'):
self._searchWidget = SearchWidget()
self._searchWidget.setSearchRoot(self.model.path)
self._fieldLabel = QtGui.QLabel('Field to plot')
self._fieldEdit = QtGui.QLineEdit()
self._fieldEdit.returnPressed.connect(self._searchWidget.searchSlot)
self._selectionPane = QtGui.QWidget()
layout = QtGui.QHBoxLayout()
layout.addWidget(self._fieldLabel)
layout.addWidget(self._fieldEdit)
self._searchWidget.layout().addLayout(layout)
self._selectionPane = self._searchWidget
self._selectionPane.layout().addStretch(1)
return self._selectionPane
def getOperationsPane(self):
"""TODO: complete this"""
if hasattr(self, 'operationsPane'):
return self.operationsPane
self.operationsPane = QtGui.QWidget()
self._createTablesButton = QtGui.QPushButton('Create tables for recording selected fields', self.operationsPane)
self._createTablesButton.clicked.connect(self.setupRecording)
layout = QtGui.QVBoxLayout()
self.operationsPane.setLayout(layout)
layout.addWidget(self._createTablesButton)
return self.operationsPane
def getFieldSelectionDock(self):
if not hasattr(self, '_fieldSelectionDock'):
self._fieldSelectionDock = QtGui.QDockWidget('Search and select elements')
self._fieldSelectionWidget = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
self._fieldSelectionWidget.setLayout(layout)
layout.addWidget(self.getSelectionPane())
layout.addWidget(self.getOperationsPane())
self._fieldSelectionDock.setWidget(self._fieldSelectionWidget)
return self._fieldSelectionDock
def getCentralWidget(self):
if not hasattr(self, '_centralWidget') or self._centralWidget is None:
self._centralWidget = PlotSelectionWidget(self.model, self.graph)
self.getSelectionPane().executed.connect(self.selectElements)
return self._centralWidget
def selectElements(self, elements):
"""Refines the selection.
Currently checks if _fieldEdit has an entry and if so, selects
only elements which have that field, and ticks the same in the
PlotSelectionWidget.
"""
field = str(self._fieldEdit.text()).strip()
if len(field) == 0:
self.getCentralWidget().setSelectedElements(elements)
return
classElementDict = defaultdict(list)
for epath in elements:
el = moose.element(epath)
classElementDict[el.className].append(el)
refinedList = []
elementFieldList = []
for className, elist in classElementDict.items():
if field in elist[0].getFieldNames('valueFinfo'):
refinedList +=elist
elementFieldList += [(el, field) for el in elist]
self.getCentralWidget().setSelectedElements(refinedList)
self.getCentralWidget().setSelectedFields(elementFieldList)
def setupRecording(self):
"""Create the tables for recording selected data and connect them."""
for element, field in self.getCentralWidget().getSelectedFields():
#createRecordingTable(element, field, self._recordingDict, self._reverseDict, self.dataRoot)
#harsha:CreateRecordingTable function is moved to python/moose/utils.py file as create function
#as this is required when I drop table on to the plot
utils.create(self.plugin.modelRoot,moose.element(element),field,"Table2")
#self.dataTable.create(self.plugin.modelRoot, moose.element(element), field)
#self.updateCallback()
def createRecordingTable(self, element, field):
"""Create table to record `field` from element `element`
Tables are created under `dataRoot`, the names are generally
created by removing `/model` in the beginning of `elementPath`
and replacing `/` with `_`. If this conflicts with an existing
table, the id value of the target element (elementPath) is
appended to the name.
"""
if len(field) == 0 or ((element, field) in self._recordingDict):
return
# The table path is not foolproof - conflict is
# possible: e.g. /model/test_object and
# /model/test/object will map to same table. So we
# check for existing table without element field
# path in recording dict.
relativePath = element.path.partition('/model[0]/')[-1]
if relativePath.startswith('/'):
relativePath = relativePath[1:]
#Convert to camelcase
if field == "concInit":
field = "ConcInit"
elif field == "conc":
field = "Conc"
elif field == "nInit":
field = "NInit"
elif field == "n":
field = "N"
elif field == "volume":
field = "Volume"
elif field == "diffConst":
field ="DiffConst"
tablePath = relativePath.replace('/', '_') + '.' + field
tablePath = re.sub('.', lambda m: {'[':'_', ']':'_'}.get(m.group(), m.group()),tablePath)
tablePath = self.dataRoot + '/' +tablePath
if moose.exists(tablePath):
tablePath = '%s_%d' % (tablePath, element.getId().value)
if not moose.exists(tablePath):
table = moose.Table(tablePath)
print 'Created', table.path, 'for plotting', '%s.%s' % (element.path, field)
target = element
moose.connect(table, 'requestOut', target, 'get%s' % (field))
self._recordingDict[(target, field)] = table
self._reverseDict[table] = (target, field)
'''
class PlotSelectionWidget(QtGui.QScrollArea):
"""Widget showing the fields of specified elements and their plottable
fields. User can select any number of fields for plotting and click a
button to generate the tables for recording data.
The data tables are by default created under /data. One can call
setDataRoot with a path to specify alternate location.
"""
def __init__(self, model, graph, *args):
QtGui.QScrollArea.__init__(self, *args)
self.model = moose.element(model.path + "/model")
self.modelRoot = self.model.path
self.setLayout(QtGui.QVBoxLayout(self))
self.layout().addWidget(self.getPlotListWidget())
self.setDataRoot(self.model.path)
self._elementWidgetsDict = {} # element path to corresponding qlabel and fields combo
def getPlotListWidget(self):
"""An internal widget to display the list of elements and their
plottable fields in comboboxes."""
if not hasattr(self, '_plotListWidget'):
self._plotListWidget = QtGui.QWidget(self)
layout = QtGui.QGridLayout(self._plotListWidget)
self._plotListWidget.setLayout(layout)
layout.addWidget(QtGui.QLabel('<h1>Elements matching search criterion will be listed here</h1>'), 0, 0)
return self._plotListWidget
def setSelectedElements(self, elementlist):
"""Create a grid of widgets displaying paths of elements in
`elementlist` if it has at least one plottable field (a field
with a numeric value). The numeric fields are listed in a
combobox next to the element path and can be selected for
plotting by the user.
"""
for ii in range(self.getPlotListWidget().layout().count()):
item = self.getPlotListWidget().layout().itemAt(ii)
if item is None:
continue
self.getPlotListWidget().layout().removeItem(item)
w = item.widget()
w.hide()
del w
del item
self._elementWidgetsDict.clear()
label = QtGui.QLabel('Element')
label.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.getPlotListWidget().layout().addWidget(label, 0, 0, 1, 2)
self.getPlotListWidget().layout().addWidget(QtGui.QLabel('Fields to plot'), 0, 2, 1, 1)
for ii, entry in enumerate(elementlist):
el = moose.element(entry)
plottableFields = []
for field, dtype in moose.getFieldDict(el.className, 'valueFinfo').items():
if dtype == 'double':
plottableFields.append(field)
if len(plottableFields) == 0:
continue
elementLabel = QtGui.QLabel(el.path)
fieldsCombo = CheckComboBox(self)
fieldsCombo.addItem('')
for item in plottableFields:
fieldsCombo.addItem(item)
self.getPlotListWidget().layout().addWidget(elementLabel, ii+1, 0, 1, 2)
self.getPlotListWidget().layout().addWidget(fieldsCombo, ii+1, 2, 1, 1)
self._elementWidgetsDict[el] = (elementLabel, fieldsCombo)
def setModelRoot(self, root):
pass
def setDataRoot(self, path):
"""The tables will be created under dataRoot"""
pass
self.dataRoot = path
def getSelectedFields(self):
"""Returns a list containing (element, field) for all selected fields"""
ret = []
for el, widgets in self._elementWidgetsDict.items():
combo = widgets[1]
for ii in range(combo.count()):
field = str(combo.itemText(ii)).strip()
if len(field) == 0:
continue
checked, success = combo.itemData(ii, Qt.CheckStateRole).toInt()
if success and checked == Qt.Checked:
ret.append((el, field))
return ret
def setSelectedFields(self, elementFieldList):
"""Set the checked fields for each element in elementFieldList.
elementFieldList: ((element1, field1), (element2, field2), ...)
"""
for el, field in elementFieldList:
combo = self._elementWidgetsDict[el][1]
idx = combo.findText(field)
if idx >= 0:
combo.setItemData(idx, QtCore.QVariant(Qt.Checked), Qt.CheckStateRole)
combo.setCurrentIndex(idx)
#
# default.py ends here