Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1992 corfunc perspective needs export and report capabilities #2065

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
3675319
Cleaning: sasview cannot be run from __main__ like this
lucas-wilkins May 26, 2022
83e4622
Minor formatting
lucas-wilkins Jun 6, 2022
0b58047
Started abstracting Perspective out of perspectives
lucas-wilkins Jun 8, 2022
5174689
Mixin class for perspectives and notes about future changes
lucas-wilkins Jun 8, 2022
6f45e6f
Mixed in Perspective mixin
lucas-wilkins Jun 8, 2022
442e15f
Perspective base class properties
lucas-wilkins Jun 8, 2022
e05a3f9
Reports are now generated through Perspective.getReport
lucas-wilkins Jun 8, 2022
2af1c96
Adding data structure for representing reports
lucas-wilkins Jun 9, 2022
4925431
Started using ReportData object
lucas-wilkins Jun 9, 2022
4012984
Minor changes
lucas-wilkins Jun 9, 2022
b458aea
Fixed getter/setter pattern
lucas-wilkins Jun 9, 2022
3a2b97d
Removed type hints from PlotHelper until this can be refactored
lucas-wilkins Jun 9, 2022
97a955c
Fixed metaclass issues and property level descriptions
lucas-wilkins Jun 9, 2022
324c823
Debugging - safer version of currentTab functionality, removed overze…
lucas-wilkins Jun 9, 2022
f7c38ca
Assertions no longer needed as data is now properly structured
lucas-wilkins Jun 9, 2022
0dc165c
Reporting system fully operational again
lucas-wilkins Jun 9, 2022
51c167f
Removed debugging print statement
lucas-wilkins Jun 9, 2022
adb834d
Towards menu functionality for reports
lucas-wilkins Jun 9, 2022
8fb75be
Fitting menu stuff done through Perspective ABC property
lucas-wilkins Jun 9, 2022
2d535fa
Basic report class structure
lucas-wilkins Jun 10, 2022
80c4279
Basic report functionality ready
lucas-wilkins Jun 15, 2022
59a2655
placeholder report in place for corfunc
lucas-wilkins Jun 15, 2022
f242feb
Some more substance to corfunc
lucas-wilkins Jun 15, 2022
83869cf
Basic report complete
lucas-wilkins Jun 15, 2022
68e96dd
Updated report content
lucas-wilkins Jun 16, 2022
831b921
Changed report window name to something more general
lucas-wilkins Jun 16, 2022
4ff3f85
Added package spec to .ymls
lucas-wilkins Jun 16, 2022
b22dd8e
Merge branch 'main' into 1992-corfunc-perspective-needs-export-and-re…
lucas-wilkins Jun 16, 2022
23101c8
name to lowercase
lucas-wilkins Jun 16, 2022
c4e0bd3
Added images, still need to make them save correctly
lucas-wilkins Jun 21, 2022
9e0393c
Merge branch 'main' into 1992-corfunc-perspective-needs-export-and-re…
lucas-wilkins Jun 21, 2022
d57b3c6
Improvements to report classy
lucas-wilkins Jun 21, 2022
ac9020a
Added dominate to requirements.txt
lucas-wilkins Jun 21, 2022
a34c594
Reports now contain vector images
lucas-wilkins Jun 22, 2022
9bd431e
Added weasyprint dependency - see issue #2034
lucas-wilkins Jun 22, 2022
b9b494f
Returned to xhtml2pdf for pdf export
lucas-wilkins Jun 22, 2022
9d86c1f
Fixed name issue
lucas-wilkins Jun 22, 2022
557f3ff
Really fixed name issues
lucas-wilkins Jun 22, 2022
a31351c
Moved report stuff to a single location
lucas-wilkins Jun 22, 2022
e69d745
Formating of report, added CSS
lucas-wilkins Jun 23, 2022
75fca95
some notes in reports
lucas-wilkins Jun 24, 2022
9060e94
fixed an encoding issue, excluded tests from gitignore
lucas-wilkins Jun 24, 2022
dd4b5c2
Simplified saving procedure, some notes
lucas-wilkins Jun 24, 2022
f71b051
Don't use regex to parse html - https://stackoverflow.com/questions/1…
lucas-wilkins Jun 24, 2022
6874a11
Tidying up - commenting out separate image things, as they are probab…
lucas-wilkins Jun 24, 2022
a92b79c
HTML Character substitutions now work with *some* hex and decimal
lucas-wilkins Jun 24, 2022
a7b2a54
Todo about case sensitivity
lucas-wilkins Jun 24, 2022
1fa5300
Final (??) clean-up
lucas-wilkins Jun 24, 2022
1f2c6c3
A couple of type hints
lucas-wilkins Jun 24, 2022
f00c619
Fixed import statements from bad refactoring
lucas-wilkins Jul 7, 2022
69f9200
Added packages to release.yml
lucas-wilkins Jul 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/installers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ jobs:
python -m pip install numpy scipy==1.7.3 docutils "pytest<6" sphinx unittest-xml-reporting
python -m pip install tinycc h5py sphinx pyparsing html5lib reportlab==3.6.6 pybind11 appdirs
python -m pip install six numba mako ipython qtconsole xhtml2pdf unittest-xml-reporting pylint
python -m pip install qt5reactor periodictable uncertainties debugpy
python -m pip install qt5reactor periodictable uncertainties debugpy dominate


- name: Install PyQt (Windows + Linux)
if: ${{ matrix.os != 'macos-latest' }}
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ jobs:
python -m pip install numpy scipy==1.7.3 docutils "pytest<6" sphinx unittest-xml-reporting
python -m pip install tinycc h5py sphinx pyparsing html5lib reportlab==3.6.6 pybind11 appdirs
python -m pip install six numba mako ipython qtconsole xhtml2pdf unittest-xml-reporting pylint
python -m pip install qt5reactor periodictable uncertainties debugpy
python -m pip install qt5reactor periodictable uncertainties debugpy dominate importlib_resources
python -m pip install html2text


- name: Install PyQt (Windows + Linux)
if: ${{ matrix.os != 'macos-latest' }}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ default_categories.json
**/logs
tests.log

# Test reports
/src/sas/qtgui/Utilities/Reports/report_test*

# Installer files
/sasview-install
/installers/build
Expand Down
4 changes: 4 additions & 0 deletions build_tools/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ matplotlib==3.4.3; sys_platform == 'darwin'
lxml
pytools
cffi
dominate
html5lib
importlib-resources
importlib_resources
42 changes: 27 additions & 15 deletions src/sas/qtgui/MainWindow/DataExplorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ def enableGraphCombo(self, combo_text):
"""
Enables/disables "Assign Plot" elements
"""
self.cbgraph.setEnabled(len(PlotHelper.currentPlots()) > 0)
self.cmdAppend.setEnabled(len(PlotHelper.currentPlots()) > 0)
self.cbgraph.setEnabled(len(PlotHelper.currentPlotIds()) > 0)
self.cmdAppend.setEnabled(len(PlotHelper.currentPlotIds()) > 0)

def initPerspectives(self):
"""
Expand Down Expand Up @@ -956,7 +956,7 @@ def updateGraphCount(self, graphs):
deleted graphs
"""
graph2, delete = graphs
graph_list = PlotHelper.currentPlots()
graph_list = PlotHelper.currentPlotIds()
self.updateGraphCombo(graph_list)

if not self.active_plots:
Expand All @@ -983,11 +983,21 @@ def updatePerspectiveCombo(self, index):
"""
Notify the gui manager about the new perspective chosen.
"""

# Notify via communicator
self.communicator.perspectiveChangedSignal.emit(self.cbFitting.itemText(index))
self.chkBatch.setEnabled(self.parent.perspective().allowBatch())
# Deactivate and uncheck the swap data option if the current perspective does not allow it
self.chkSwap.setEnabled(self.parent.perspective().allowSwap())
if not self.parent.perspective().allowSwap():

# Set checkboxes
current_perspective = self.parent.perspective()

allow_batch = False if current_perspective is None else current_perspective.allowBatch()
allow_swap = False if current_perspective is None else current_perspective.allowSwap()

self.chkBatch.setEnabled(allow_batch)
self.chkSwap.setEnabled(allow_swap)

# Using this conditional prevents the checkbox for going into the "neither checked nor unchecked" state
if not allow_swap:
self.chkSwap.setCheckState(False)

def itemFromDisplayName(self, name):
Expand Down Expand Up @@ -1213,7 +1223,7 @@ def appendPlot(self):
# old plot data
plot_id = str(self.cbgraph.currentText())
try:
assert plot_id in PlotHelper.currentPlots(), "No such plot: %s" % (plot_id)
assert plot_id in PlotHelper.currentPlotIds(), "No such plot: %s" % (plot_id)
except:
return

Expand Down Expand Up @@ -1828,7 +1838,7 @@ def closeAllPlots(self):
Close all currently displayed plots
"""

for plot_id in PlotHelper.currentPlots():
for plot_id in PlotHelper.currentPlotIds():
try:
plotter = PlotHelper.plotById(plot_id)
plotter.close()
Expand All @@ -1841,7 +1851,7 @@ def minimizeAllPlots(self):
"""
Minimize all currently displayed plots
"""
for plot_id in PlotHelper.currentPlots():
for plot_id in PlotHelper.currentPlotIds():
plotter = PlotHelper.plotById(plot_id)
plotter.showMinimized()

Expand All @@ -1853,7 +1863,7 @@ def closePlotsForItem(self, item):

# {} -> 'Graph1' : HashableStandardItem()
current_plot_items = {}
for plot_name in PlotHelper.currentPlots():
for plot_name in PlotHelper.currentPlotIds():
current_plot_items[plot_name] = PlotHelper.plotById(plot_name).item

# item and its hashable children
Expand Down Expand Up @@ -1883,15 +1893,17 @@ def closePlotsForItem(self, item):

pass # debugger anchor

def onAnalysisUpdate(self, new_perspective=""):
def onAnalysisUpdate(self, new_perspective_name: str):
"""
Update the perspective combo index based on passed string
"""
assert new_perspective in Perspectives.PERSPECTIVES.keys()
assert new_perspective_name in Perspectives.PERSPECTIVES.keys()

self.cbFitting.blockSignals(True)
self.cbFitting.setCurrentIndex(self.cbFitting.findText(new_perspective))
index = self.cbFitting.findText(new_perspective_name)
self.cbFitting.setCurrentIndex(index)
self.cbFitting.blockSignals(False)
pass


def loadComplete(self, output):
"""
Expand Down
156 changes: 88 additions & 68 deletions src/sas/qtgui/MainWindow/GuiManager.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import sys
import os
import subprocess
import logging
import json
import webbrowser
import traceback

from typing import Optional

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt, QLocale, QUrl
from PyQt5.QtCore import Qt, QLocale

import matplotlib as mpl
mpl.use("Qt5Agg")
Expand All @@ -18,7 +19,7 @@

from twisted.internet import reactor
# General SAS imports
from sas import get_local_config, get_custom_config
from sas import get_custom_config
from sas.qtgui.Utilities.ConnectionProxy import ConnectionProxy
from sas.qtgui.Utilities.SasviewLogger import setup_qt_logging

Expand All @@ -31,7 +32,7 @@
from sas.qtgui.Utilities.GridPanel import BatchOutputPanel
from sas.qtgui.Utilities.ResultPanel import ResultPanel

from sas.qtgui.Utilities.ReportDialog import ReportDialog
from sas.qtgui.Utilities.Reports.ReportDialog import ReportDialog
from sas.qtgui.MainWindow.Acknowledgements import Acknowledgements
from sas.qtgui.MainWindow.AboutBox import AboutBox
from sas.qtgui.MainWindow.WelcomePanel import WelcomePanel
Expand All @@ -48,12 +49,19 @@
from sas.qtgui.Calculators.ResolutionCalculatorPanel import ResolutionCalculatorPanel
from sas.qtgui.Calculators.DataOperationUtilityPanel import DataOperationUtilityPanel


import sas.qtgui.Plotting.PlotHelper as PlotHelper

# Perspectives
import sas.qtgui.Perspectives as Perspectives
from sas.qtgui.Perspectives.perspective import Perspective

from sas.qtgui.Perspectives.Fitting.FittingPerspective import FittingWindow
from sas.qtgui.MainWindow.DataExplorer import DataExplorerWindow, DEFAULT_PERSPECTIVE
from sas.qtgui.Perspectives.Corfunc.CorfuncPerspective import CorfuncWindow
from sas.qtgui.Perspectives.Invariant.InvariantPerspective import InvariantWindow
from sas.qtgui.Perspectives.Inversion.InversionPerspective import InversionWindow

from sas.qtgui.MainWindow.DataExplorer import DataExplorerWindow

from sas.qtgui.Utilities.AddMultEditor import AddMultEditor
from sas.qtgui.Utilities.ImageViewer import ImageViewer
Expand All @@ -62,7 +70,7 @@
logger = logging.getLogger(__name__)


class GuiManager(object):
class GuiManager:
"""
Main SasView window functionality
"""
Expand Down Expand Up @@ -93,7 +101,7 @@ def __init__(self, parent=None):
self.addTriggers()

# Currently displayed perspective
self._current_perspective = None
self._current_perspective: Optional[Perspective] = None
self.loadedPerspectives = {}

# Populate the main window with stuff
Expand Down Expand Up @@ -259,7 +267,7 @@ def plotSelectedSlot(self, plot_name):
Set focus on the selected plot
"""
# loop over all visible plots and find the requested plot
for plot in PlotHelper.currentPlots():
for plot in PlotHelper.currentPlotIds():
# take last plot
if PlotHelper.plotById(plot).data[-1].name == plot_name:
# set focus on the plot
Expand Down Expand Up @@ -341,32 +349,82 @@ def workspace(self):
"""
return self._workspace.workspace

def perspectiveChanged(self, perspective_name):
def perspectiveChanged(self, new_perspective_name: str):
"""
Respond to change of the perspective signal
"""
# Remove the previous perspective from the window
self.clearPerspectiveMenubarOptions(self._current_perspective)
if self._current_perspective:

assert new_perspective_name in self.loadedPerspectives # supplied name should always be in loaded perspectives

# Uncheck all menu items
for menuItem in self._workspace.menuAnalysis.actions():
menuItem.setChecked(False)

if self._current_perspective is not None:

# Remove the fitting menu for now, will be replaced later if we move back to a perspective that supports it
# I do not like that this requires the menu action to exist to be correct
if self._current_perspective.supports_fitting_menu:
self._workspace.menubar.removeAction(self._workspace.menuFitting.menuAction())

# Remove perspective and store in Perspective dictionary
self.loadedPerspectives[
self._current_perspective.name] = self._current_perspective
self.loadedPerspectives[self._current_perspective.name] = self._current_perspective

self._workspace.workspace.removeSubWindow(self._current_perspective)
self._workspace.workspace.removeSubWindow(self.subwindow)
# Get new perspective
self._current_perspective = self.loadedPerspectives[str(perspective_name)]

self.setupPerspectiveMenubarOptions(self._current_perspective)
# Get new perspective - note that _current_perspective is of type Optional[Perspective],
# but new_perspective is of type Perspective, thus call to Perspective members are safe
new_perspective = self.loadedPerspectives[new_perspective_name]

self.subwindow = self._workspace.workspace.addSubWindow(
self._current_perspective)
self._workspace.actionReport.setEnabled(new_perspective.supports_reports)
self._workspace.actionOpen_Analysis.setEnabled(False)
self._workspace.actionSave_Analysis.setEnabled(False)

if new_perspective.isSerializable():
self._workspace.actionOpen_Analysis.setEnabled(True)
self._workspace.actionSave_Analysis.setEnabled(True)

if new_perspective.supports_fitting_menu:
# Put the fitting menu back in
# This is a bit involved but it is needed to preserve the menu ordering
self._workspace.menubar.removeAction(self._workspace.menuWindow.menuAction())
self._workspace.menubar.removeAction(self._workspace.menuHelp.menuAction())

self._workspace.menubar.addAction(self._workspace.menuFitting.menuAction())

self._workspace.menubar.addAction(self._workspace.menuWindow.menuAction())
self._workspace.menubar.addAction(self._workspace.menuHelp.menuAction())

#
# Selection on perspective choice menu
#
if isinstance(new_perspective, FittingWindow):
self.checkAnalysisOption(self._workspace.actionFitting)

elif isinstance(new_perspective, InvariantWindow):
self.checkAnalysisOption(self._workspace.actionInvariant)

elif isinstance(new_perspective, InversionWindow):
self.checkAnalysisOption(self._workspace.actionInversion)

elif isinstance(new_perspective, CorfuncWindow):
self.checkAnalysisOption(self._workspace.actionCorfunc)


#
# Set up the window
#
self.subwindow = self._workspace.workspace.addSubWindow(new_perspective)

# Resize to the workspace height
workspace_height = self._workspace.workspace.sizeHint().height()
perspective_size = self._current_perspective.sizeHint()
perspective_size = new_perspective.sizeHint()
perspective_width = perspective_size.width()
self._current_perspective.resize(perspective_width, workspace_height-10)
new_perspective.resize(perspective_width, workspace_height-10)

# Set the current perspective to new one and show
self._current_perspective = new_perspective
self._current_perspective.show()

def updatePerspective(self, data):
Expand Down Expand Up @@ -773,16 +831,16 @@ def actionReport(self):
"""
Show the Fit Report dialog.
"""
report_list = None
if getattr(self._current_perspective, "currentTab"):
try:
report_list = self._current_perspective.currentTab.getReport()
except Exception as ex:
logging.error("Report generation failed with: " + str(ex))
if self._current_perspective is not None:
report_data = self._current_perspective.getReport()

if report_data is None:
logging.info("Report data is empty, dialog not shown")
else:
self.report_dialog = ReportDialog(report_data=report_data, parent=self._parent)
self.report_dialog.show()


if report_list is not None:
self.report_dialog = ReportDialog(parent=self, report_list=report_list)
self.report_dialog.show()

def actionReset(self):
"""
Expand Down Expand Up @@ -1237,44 +1295,6 @@ def checkAnalysisOption(self, analysisMenuOption):
self.uncheckAllMenuItems(self._workspace.menuAnalysis)
analysisMenuOption.setChecked(True)

def clearPerspectiveMenubarOptions(self, perspective):
"""
When closing a perspective, clears the menu bar
"""
for menuItem in self._workspace.menuAnalysis.actions():
menuItem.setChecked(False)

if isinstance(self._current_perspective, Perspectives.PERSPECTIVES["Fitting"]):
self._workspace.menubar.removeAction(self._workspace.menuFitting.menuAction())

def setupPerspectiveMenubarOptions(self, perspective):
"""
When setting a perspective, sets up the menu bar
"""
self._workspace.actionReport.setEnabled(False)
self._workspace.actionOpen_Analysis.setEnabled(False)
self._workspace.actionSave_Analysis.setEnabled(False)
if hasattr(perspective, 'isSerializable') and perspective.isSerializable():
self._workspace.actionOpen_Analysis.setEnabled(True)
self._workspace.actionSave_Analysis.setEnabled(True)

if isinstance(perspective, Perspectives.PERSPECTIVES["Fitting"]):
self.checkAnalysisOption(self._workspace.actionFitting)
# Put the fitting menu back in
# This is a bit involved but it is needed to preserve the menu ordering
self._workspace.menubar.removeAction(self._workspace.menuWindow.menuAction())
self._workspace.menubar.removeAction(self._workspace.menuHelp.menuAction())
self._workspace.menubar.addAction(self._workspace.menuFitting.menuAction())
self._workspace.menubar.addAction(self._workspace.menuWindow.menuAction())
self._workspace.menubar.addAction(self._workspace.menuHelp.menuAction())
self._workspace.actionReport.setEnabled(True)

elif isinstance(perspective, Perspectives.PERSPECTIVES["Invariant"]):
self.checkAnalysisOption(self._workspace.actionInvariant)
elif isinstance(perspective, Perspectives.PERSPECTIVES["Inversion"]):
self.checkAnalysisOption(self._workspace.actionInversion)
elif isinstance(perspective, Perspectives.PERSPECTIVES["Corfunc"]):
self.checkAnalysisOption(self._workspace.actionCorfunc)

def saveCustomConfig(self):
"""
Expand Down
Loading