source: sasview/src/sas/qtgui/MainWindow/GuiManager.py @ 2b39fea

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 2b39fea was 2b39fea, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

Missed QWorkspace → QMdiArea method renaming

  • Property mode set to 100644
File size: 32.8 KB
RevLine 
[f721030]1import sys
[0cd8612]2import os
[9e426c1]3import subprocess
4import logging
5import json
6import webbrowser
[f721030]7
[4992ff2]8from PyQt5.QtWidgets import *
9from PyQt5.QtGui import *
[d6b8a1d]10from PyQt5.QtCore import Qt, QLocale, QUrl
[1042dba]11
12from twisted.internet import reactor
[dc5ef15]13# General SAS imports
14from sas.qtgui.Utilities.ConnectionProxy import ConnectionProxy
[83eb5208]15from sas.qtgui.Utilities.SasviewLogger import XStream
[fef38e8]16
[83eb5208]17import sas.qtgui.Utilities.LocalConfig as LocalConfig
18import sas.qtgui.Utilities.GuiUtils as GuiUtils
19
[fef38e8]20import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary
[3b3b40b]21from sas.qtgui.Utilities.TabbedModelEditor import TabbedModelEditor
22from sas.qtgui.Utilities.PluginManager import PluginManager
[d4dac80]23from sas.qtgui.Utilities.GridPanel import BatchOutputPanel
24
[57be490]25from sas.qtgui.Utilities.ReportDialog import ReportDialog
[83eb5208]26from sas.qtgui.MainWindow.UI.AcknowledgementsUI import Ui_Acknowledgements
27from sas.qtgui.MainWindow.AboutBox import AboutBox
28from sas.qtgui.MainWindow.WelcomePanel import WelcomePanel
[fef38e8]29
[dc5ef15]30from sas.qtgui.MainWindow.DataManager import DataManager
[83eb5208]31
32from sas.qtgui.Calculators.SldPanel import SldPanel
33from sas.qtgui.Calculators.DensityPanel import DensityPanel
34from sas.qtgui.Calculators.KiessigPanel import KiessigPanel
35from sas.qtgui.Calculators.SlitSizeCalculator import SlitSizeCalculator
[28a09b0]36from sas.qtgui.Calculators.GenericScatteringCalculator import GenericScatteringCalculator
[01cda57]37from sas.qtgui.Calculators.ResolutionCalculatorPanel import ResolutionCalculatorPanel
[d5c5d3d]38from sas.qtgui.Calculators.DataOperationUtilityPanel import DataOperationUtilityPanel
[f721030]39
40# Perspectives
[83eb5208]41import sas.qtgui.Perspectives as Perspectives
[6c8fb2c]42from sas.qtgui.Perspectives.Fitting.FittingPerspective import FittingWindow
[d4881f6a]43from sas.qtgui.MainWindow.DataExplorer import DataExplorerWindow, DEFAULT_PERSPECTIVE
[f51ed67]44
[01ef3f7]45from sas.qtgui.Utilities.AddMultEditor import AddMultEditor
46
[4992ff2]47class Acknowledgements(QDialog, Ui_Acknowledgements):
[f51ed67]48    def __init__(self, parent=None):
[4992ff2]49        QDialog.__init__(self, parent)
[f51ed67]50        self.setupUi(self)
[f721030]51
52class GuiManager(object):
53    """
54    Main SasView window functionality
55    """
[6fd4e36]56    def __init__(self, parent=None):
[f721030]57        """
[257bd57]58        Initialize the manager as a child of MainWindow.
[f721030]59        """
[6fd4e36]60        self._workspace = parent
[f721030]61        self._parent = parent
62
[d6b8a1d]63        # Decide on a locale
64        QLocale.setDefault(QLocale('en_US'))
65
[f721030]66        # Add signal callbacks
67        self.addCallbacks()
68
[c889a3e]69        # Assure model categories are available
70        self.addCategories()
71
[f721030]72        # Create the data manager
[1042dba]73        # TODO: pull out all required methods from DataManager and reimplement
74        self._data_manager = DataManager()
[f721030]75
76        # Create action triggers
77        self.addTriggers()
78
[d4881f6a]79        # Currently displayed perspective
[5236449]80        self._current_perspective = None
81
[d4881f6a]82        # Populate the main window with stuff
[0cd8612]83        self.addWidgets()
[f721030]84
[0cd8612]85        # Fork off logging messages to the Log Window
[8cb6cd6]86        XStream.stdout().messageWritten.connect(self.listWidget.insertPlainText)
87        XStream.stderr().messageWritten.connect(self.listWidget.insertPlainText)
88
[0cd8612]89        # Log the start of the session
90        logging.info(" --- SasView session started ---")
91        # Log the python version
92        logging.info("Python: %s" % sys.version)
[9e426c1]93
[e540cd2]94        # Set up the status bar
95        self.statusBarSetup()
[f721030]96
[9e426c1]97        # Current tutorial location
[b0c5e8c]98        self._tutorialLocation = os.path.abspath(os.path.join(GuiUtils.HELP_DIRECTORY_LOCATION,
[9e426c1]99                                              "_downloads",
[31c5b58]100                                              "Tutorial.pdf"))
[8353d90]101
[0cd8612]102    def addWidgets(self):
103        """
104        Populate the main window with widgets
105
106        TODO: overwrite close() on Log and DR widgets so they can be hidden/shown
107        on request
108        """
109        # Add FileDialog widget as docked
[630155bd]110        self.filesWidget = DataExplorerWindow(self._parent, self, manager=self._data_manager)
[2a432e7]111        ObjectLibrary.addObject('DataExplorer', self.filesWidget)
[0cd8612]112
[4992ff2]113        self.dockedFilesWidget = QDockWidget("Data Explorer", self._workspace)
[7969b9c]114        self.dockedFilesWidget.setFloating(False)
[0cd8612]115        self.dockedFilesWidget.setWidget(self.filesWidget)
[83d6249]116
[0cd8612]117        # Disable maximize/minimize and close buttons
[4992ff2]118        self.dockedFilesWidget.setFeatures(QDockWidget.NoDockWidgetFeatures)
119
[7969b9c]120        #self._workspace.workspace.addDockWidget(Qt.LeftDockWidgetArea, self.dockedFilesWidget)
121        self._workspace.addDockWidget(Qt.LeftDockWidgetArea, self.dockedFilesWidget)
[0cd8612]122
123        # Add the console window as another docked widget
[4992ff2]124        self.logDockWidget = QDockWidget("Log Explorer", self._workspace)
[0cd8612]125        self.logDockWidget.setObjectName("LogDockWidget")
[4992ff2]126
127        self.listWidget = QTextBrowser()
[0cd8612]128        self.logDockWidget.setWidget(self.listWidget)
[7969b9c]129        self._workspace.addDockWidget(Qt.BottomDockWidgetArea, self.logDockWidget)
[0cd8612]130
131        # Add other, minor widgets
132        self.ackWidget = Acknowledgements()
133        self.aboutWidget = AboutBox()
[8353d90]134        self.welcomePanel = WelcomePanel()
[d4dac80]135        self.grid_window = None
[a0ed202]136        self._workspace.toolBar.setVisible(LocalConfig.TOOLBAR_SHOW)
137        self._workspace.actionHide_Toolbar.setText("Show Toolbar")
[0cd8612]138
[1d85b5e]139        # Add calculators - floating for usability
140        self.SLDCalculator = SldPanel(self)
141        self.DVCalculator = DensityPanel(self)
[a8ec5b1]142        self.KIESSIGCalculator = KiessigPanel(self)
[abc5e70]143        self.SlitSizeCalculator = SlitSizeCalculator(self)
[28a09b0]144        self.GENSASCalculator = GenericScatteringCalculator(self)
[01cda57]145        self.ResolutionCalculator = ResolutionCalculatorPanel(self)
[d5c5d3d]146        self.DataOperation = DataOperationUtilityPanel(self)
[83d6249]147
[c889a3e]148    def addCategories(self):
149        """
150        Make sure categories.json exists and if not compile it and install in ~/.sasview
151        """
152        try:
153            from sas.sascalc.fit.models import ModelManager
154            from sas.qtgui.Utilities.CategoryInstaller import CategoryInstaller
155            model_list = ModelManager().cat_model_list()
156            CategoryInstaller.check_install(model_list=model_list)
157        except Exception:
158            logger.error("%s: could not load SasView models")
159            logger.error(traceback.format_exc())
160
[e540cd2]161    def statusBarSetup(self):
162        """
163        Define the status bar.
164        | <message label> .... | Progress Bar |
165
166        Progress bar invisible until explicitly shown
167        """
[4992ff2]168        self.progress = QProgressBar()
[e540cd2]169        self._workspace.statusbar.setSizeGripEnabled(False)
170
[4992ff2]171        self.statusLabel = QLabel()
[e540cd2]172        self.statusLabel.setText("Welcome to SasView")
[8cb6cd6]173        self._workspace.statusbar.addPermanentWidget(self.statusLabel, 1)
[e540cd2]174        self._workspace.statusbar.addPermanentWidget(self.progress, stretch=0)
[8cb6cd6]175        self.progress.setRange(0, 100)
[e540cd2]176        self.progress.setValue(0)
177        self.progress.setTextVisible(True)
178        self.progress.setVisible(False)
179
[9d266d2]180    def fileWasRead(self, data):
[f721030]181        """
[f82ab8c]182        Callback for fileDataReceivedSignal
[f721030]183        """
184        pass
[481ff26]185
[e90988c]186    def showHelp(self, url):
187        """
188        Open a local url in the default browser
189        """
190        location = GuiUtils.HELP_DIRECTORY_LOCATION + url
191        try:
192            webbrowser.open('file://' + os.path.realpath(location))
193        except webbrowser.Error as ex:
194            logging.warning("Cannot display help. %s" % ex)
195
[8cb6cd6]196    def workspace(self):
197        """
198        Accessor for the main window workspace
199        """
200        return self._workspace.workspace
201
[83d6249]202    def perspectiveChanged(self, perspective_name):
203        """
204        Respond to change of the perspective signal
205        """
206        # Close the previous perspective
[9e54199]207        self.clearPerspectiveMenubarOptions(self._current_perspective)
[83d6249]208        if self._current_perspective:
[b1e36a3]209            self._current_perspective.setClosable()
[7c487846]210            #self._workspace.workspace.removeSubWindow(self._current_perspective)
[83d6249]211            self._current_perspective.close()
[7c487846]212            self._workspace.workspace.removeSubWindow(self._current_perspective)
[83d6249]213        # Default perspective
[811bec1]214        self._current_perspective = Perspectives.PERSPECTIVES[str(perspective_name)](parent=self)
[9c391946]215
[8ac3551]216        self.setupPerspectiveMenubarOptions(self._current_perspective)
217
[d1955d67]218        subwindow = self._workspace.workspace.addSubWindow(self._current_perspective)
[4992ff2]219
[9c391946]220        # Resize to the workspace height
[fbfc488]221        workspace_height = self._workspace.workspace.sizeHint().height()
222        perspective_size = self._current_perspective.sizeHint()
223        perspective_width = perspective_size.width()
224        self._current_perspective.resize(perspective_width, workspace_height-10)
[d1955d67]225        # Resize the mdi area to match the widget within
226        subwindow.resize(subwindow.minimumSizeHint())
[7969b9c]227
[83d6249]228        self._current_perspective.show()
229
[f721030]230    def updatePerspective(self, data):
231        """
[71361f0]232        Update perspective with data sent.
[f721030]233        """
234        assert isinstance(data, list)
235        if self._current_perspective is not None:
[b3e8629]236            self._current_perspective.setData(list(data.values()))
[f721030]237        else:
238            msg = "No perspective is currently active."
239            logging.info(msg)
[481ff26]240
[f721030]241    def communicator(self):
[257bd57]242        """ Accessor for the communicator """
[f721030]243        return self.communicate
244
245    def perspective(self):
[257bd57]246        """ Accessor for the perspective """
[f721030]247        return self._current_perspective
248
[e540cd2]249    def updateProgressBar(self, value):
250        """
251        Update progress bar with the required value (0-100)
252        """
[8cb6cd6]253        assert -1 <= value <= 100
[e540cd2]254        if value == -1:
255            self.progress.setVisible(False)
256            return
257        if not self.progress.isVisible():
258            self.progress.setTextVisible(True)
259            self.progress.setVisible(True)
260
261        self.progress.setValue(value)
262
[f721030]263    def updateStatusBar(self, text):
264        """
[71361f0]265        Set the status bar text
[f721030]266        """
[e540cd2]267        self.statusLabel.setText(text)
[f721030]268
[1042dba]269    def createGuiData(self, item, p_file=None):
270        """
271        Access the Data1D -> plottable Data1D conversion
272        """
273        return self._data_manager.create_gui_data(item, p_file)
[f721030]274
275    def setData(self, data):
276        """
277        Sends data to current perspective
278        """
279        if self._current_perspective is not None:
[b3e8629]280            self._current_perspective.setData(list(data.values()))
[f721030]281        else:
282            msg = "Guiframe does not have a current perspective"
283            logging.info(msg)
284
[d4dac80]285    def findItemFromFilename(self, filename):
286        """
287        Queries the data explorer for the index corresponding to the filename within
288        """
289        return self.filesWidget.itemFromFilename(filename)
290
[9e426c1]291    def quitApplication(self):
292        """
293        Close the reactor and exit nicely.
294        """
295        # Display confirmation messagebox
296        quit_msg = "Are you sure you want to exit the application?"
[4992ff2]297        reply = QMessageBox.question(
[481ff26]298            self._parent,
[7451b88]299            'Information',
[481ff26]300            quit_msg,
[4992ff2]301            QMessageBox.Yes,
302            QMessageBox.No)
[9e426c1]303
304        # Exit if yes
[4992ff2]305        if reply == QMessageBox.Yes:
[7451b88]306            reactor.callFromThread(reactor.stop)
307            return True
308
309        return False
[9e426c1]310
311    def checkUpdate(self):
312        """
313        Check with the deployment server whether a new version
314        of the application is available.
315        A thread is started for the connecting with the server. The thread calls
316        a call-back method when the current version number has been obtained.
317        """
318        version_info = {"version": "0.0.0"}
[dc5ef15]319        c = ConnectionProxy(LocalConfig.__update_URL__, LocalConfig.UPDATE_TIMEOUT)
[9e426c1]320        response = c.connect()
[71361f0]321        if response is None:
322            return
323        try:
324            content = response.read().strip()
325            logging.info("Connected to www.sasview.org. Latest version: %s"
326                            % (content))
327            version_info = json.loads(content)
328            self.processVersion(version_info)
[b3e8629]329        except ValueError as ex:
[71361f0]330            logging.info("Failed to connect to www.sasview.org:", ex)
[481ff26]331
[f82ab8c]332    def processVersion(self, version_info):
[9e426c1]333        """
334        Call-back method for the process of checking for updates.
335        This methods is called by a VersionThread object once the current
336        version number has been obtained. If the check is being done in the
337        background, the user will not be notified unless there's an update.
338
339        :param version: version string
340        """
341        try:
342            version = version_info["version"]
343            if version == "0.0.0":
344                msg = "Could not connect to the application server."
345                msg += " Please try again later."
346                self.communicate.statusBarUpdateSignal.emit(msg)
347
[cee5c78]348            elif version.__gt__(LocalConfig.__version__):
[9e426c1]349                msg = "Version %s is available! " % str(version)
[f82ab8c]350                if "download_url" in version_info:
351                    webbrowser.open(version_info["download_url"])
[9e426c1]352                else:
[f82ab8c]353                    webbrowser.open(LocalConfig.__download_page__)
[9e426c1]354                self.communicate.statusBarUpdateSignal.emit(msg)
355            else:
356                msg = "You have the latest version"
357                msg += " of %s" % str(LocalConfig.__appname__)
358                self.communicate.statusBarUpdateSignal.emit(msg)
359        except:
360            msg = "guiframe: could not get latest application"
[b3e8629]361            msg += " version number\n  %s" % sys.exc_info()[1]
[9e426c1]362            logging.error(msg)
[f82ab8c]363            msg = "Could not connect to the application server."
364            msg += " Please try again later."
365            self.communicate.statusBarUpdateSignal.emit(msg)
[9e426c1]366
[8353d90]367    def showWelcomeMessage(self):
368        """ Show the Welcome panel """
369        self._workspace.workspace.addSubWindow(self.welcomePanel)
370        self.welcomePanel.show()
371
[f721030]372    def addCallbacks(self):
373        """
[9e426c1]374        Method defining all signal connections for the gui manager
[f721030]375        """
[0cd8612]376        self.communicate = GuiUtils.Communicate()
[9d266d2]377        self.communicate.fileDataReceivedSignal.connect(self.fileWasRead)
[f721030]378        self.communicate.statusBarUpdateSignal.connect(self.updateStatusBar)
379        self.communicate.updatePerspectiveWithDataSignal.connect(self.updatePerspective)
[e540cd2]380        self.communicate.progressBarUpdateSignal.connect(self.updateProgressBar)
[83d6249]381        self.communicate.perspectiveChangedSignal.connect(self.perspectiveChanged)
[cbcdd2c]382        self.communicate.updateTheoryFromPerspectiveSignal.connect(self.updateTheoryFromPerspective)
[d48cc19]383        self.communicate.plotRequestedSignal.connect(self.showPlot)
[3b3b40b]384        self.communicate.plotFromFilenameSignal.connect(self.showPlotFromFilename)
[d5c5d3d]385        self.communicate.updateModelFromDataOperationPanelSignal.connect(self.updateModelFromDataOperationPanel)
[f721030]386
387    def addTriggers(self):
388        """
389        Trigger definitions for all menu/toolbar actions.
390        """
391        # File
392        self._workspace.actionLoadData.triggered.connect(self.actionLoadData)
393        self._workspace.actionLoad_Data_Folder.triggered.connect(self.actionLoad_Data_Folder)
394        self._workspace.actionOpen_Project.triggered.connect(self.actionOpen_Project)
395        self._workspace.actionOpen_Analysis.triggered.connect(self.actionOpen_Analysis)
396        self._workspace.actionSave.triggered.connect(self.actionSave)
397        self._workspace.actionSave_Analysis.triggered.connect(self.actionSave_Analysis)
398        self._workspace.actionQuit.triggered.connect(self.actionQuit)
399        # Edit
400        self._workspace.actionUndo.triggered.connect(self.actionUndo)
401        self._workspace.actionRedo.triggered.connect(self.actionRedo)
402        self._workspace.actionCopy.triggered.connect(self.actionCopy)
403        self._workspace.actionPaste.triggered.connect(self.actionPaste)
404        self._workspace.actionReport.triggered.connect(self.actionReport)
405        self._workspace.actionReset.triggered.connect(self.actionReset)
406        self._workspace.actionExcel.triggered.connect(self.actionExcel)
407        self._workspace.actionLatex.triggered.connect(self.actionLatex)
408
409        # View
410        self._workspace.actionShow_Grid_Window.triggered.connect(self.actionShow_Grid_Window)
411        self._workspace.actionHide_Toolbar.triggered.connect(self.actionHide_Toolbar)
412        self._workspace.actionStartup_Settings.triggered.connect(self.actionStartup_Settings)
413        self._workspace.actionCategry_Manager.triggered.connect(self.actionCategry_Manager)
414        # Tools
415        self._workspace.actionData_Operation.triggered.connect(self.actionData_Operation)
416        self._workspace.actionSLD_Calculator.triggered.connect(self.actionSLD_Calculator)
417        self._workspace.actionDensity_Volume_Calculator.triggered.connect(self.actionDensity_Volume_Calculator)
[363fbfa]418        self._workspace.actionKeissig_Calculator.triggered.connect(self.actionKiessig_Calculator)
419        #self._workspace.actionKIESSING_Calculator.triggered.connect(self.actionKIESSING_Calculator)
[f721030]420        self._workspace.actionSlit_Size_Calculator.triggered.connect(self.actionSlit_Size_Calculator)
421        self._workspace.actionSAS_Resolution_Estimator.triggered.connect(self.actionSAS_Resolution_Estimator)
422        self._workspace.actionGeneric_Scattering_Calculator.triggered.connect(self.actionGeneric_Scattering_Calculator)
423        self._workspace.actionPython_Shell_Editor.triggered.connect(self.actionPython_Shell_Editor)
424        self._workspace.actionImage_Viewer.triggered.connect(self.actionImage_Viewer)
425        # Fitting
426        self._workspace.actionNew_Fit_Page.triggered.connect(self.actionNew_Fit_Page)
427        self._workspace.actionConstrained_Fit.triggered.connect(self.actionConstrained_Fit)
428        self._workspace.actionCombine_Batch_Fit.triggered.connect(self.actionCombine_Batch_Fit)
429        self._workspace.actionFit_Options.triggered.connect(self.actionFit_Options)
[06ce180]430        self._workspace.actionGPU_Options.triggered.connect(self.actionGPU_Options)
[f721030]431        self._workspace.actionFit_Results.triggered.connect(self.actionFit_Results)
[3b3b40b]432        self._workspace.actionAdd_Custom_Model.triggered.connect(self.actionAdd_Custom_Model)
[f721030]433        self._workspace.actionEdit_Custom_Model.triggered.connect(self.actionEdit_Custom_Model)
[3b3b40b]434        self._workspace.actionManage_Custom_Models.triggered.connect(self.actionManage_Custom_Models)
[01ef3f7]435        self._workspace.actionAddMult_Models.triggered.connect(self.actionAddMult_Models)
[f721030]436        # Window
437        self._workspace.actionCascade.triggered.connect(self.actionCascade)
[e540cd2]438        self._workspace.actionTile.triggered.connect(self.actionTile)
[f721030]439        self._workspace.actionArrange_Icons.triggered.connect(self.actionArrange_Icons)
440        self._workspace.actionNext.triggered.connect(self.actionNext)
441        self._workspace.actionPrevious.triggered.connect(self.actionPrevious)
442        # Analysis
443        self._workspace.actionFitting.triggered.connect(self.actionFitting)
444        self._workspace.actionInversion.triggered.connect(self.actionInversion)
445        self._workspace.actionInvariant.triggered.connect(self.actionInvariant)
[8ac3551]446        self._workspace.actionCorfunc.triggered.connect(self.actionCorfunc)
[f721030]447        # Help
448        self._workspace.actionDocumentation.triggered.connect(self.actionDocumentation)
449        self._workspace.actionTutorial.triggered.connect(self.actionTutorial)
450        self._workspace.actionAcknowledge.triggered.connect(self.actionAcknowledge)
451        self._workspace.actionAbout.triggered.connect(self.actionAbout)
452        self._workspace.actionCheck_for_update.triggered.connect(self.actionCheck_for_update)
453
[d4dac80]454        self.communicate.sendDataToGridSignal.connect(self.showBatchOutput)
455
[f721030]456    #============ FILE =================
457    def actionLoadData(self):
458        """
[9e426c1]459        Menu File/Load Data File(s)
[f721030]460        """
[5032ea68]461        self.filesWidget.loadFile()
[f721030]462
463    def actionLoad_Data_Folder(self):
464        """
[9e426c1]465        Menu File/Load Data Folder
[f721030]466        """
[5032ea68]467        self.filesWidget.loadFolder()
[f721030]468
469    def actionOpen_Project(self):
470        """
[630155bd]471        Menu Open Project
[f721030]472        """
[630155bd]473        self.filesWidget.loadProject()
[f721030]474
475    def actionOpen_Analysis(self):
476        """
477        """
478        print("actionOpen_Analysis TRIGGERED")
479        pass
480
481    def actionSave(self):
482        """
[630155bd]483        Menu Save Project
[f721030]484        """
[630155bd]485        self.filesWidget.saveProject()
[f721030]486
487    def actionSave_Analysis(self):
488        """
[57be490]489        Menu File/Save Analysis
[f721030]490        """
[57be490]491        self.communicate.saveAnalysisSignal.emit()
[f721030]492
493    def actionQuit(self):
494        """
[1042dba]495        Close the reactor, exit the application.
[f721030]496        """
[9e426c1]497        self.quitApplication()
[f721030]498
499    #============ EDIT =================
500    def actionUndo(self):
501        """
502        """
503        print("actionUndo TRIGGERED")
504        pass
505
506    def actionRedo(self):
507        """
508        """
509        print("actionRedo TRIGGERED")
510        pass
511
512    def actionCopy(self):
513        """
514        """
515        print("actionCopy TRIGGERED")
516        pass
517
518    def actionPaste(self):
519        """
520        """
521        print("actionPaste TRIGGERED")
522        pass
523
524    def actionReport(self):
525        """
[57be490]526        Show the Fit Report dialog.
[f721030]527        """
[57be490]528        report_list = None
529        if getattr(self._current_perspective, "currentTab"):
530            try:
531                report_list = self._current_perspective.currentTab.getReport()
532            except Exception as ex:
533                logging.error("Report generation failed with: " + str(ex))
534
535        if report_list is not None:
536            self.report_dialog = ReportDialog(parent=self, report_list=report_list)
537            self.report_dialog.show()
[f721030]538
539    def actionReset(self):
540        """
541        """
[0cd8612]542        logging.warning(" *** actionOpen_Analysis logging *******")
543        print("actionReset print TRIGGERED")
544        sys.stderr.write("STDERR - TRIGGERED")
[f721030]545        pass
546
547    def actionExcel(self):
548        """
549        """
550        print("actionExcel TRIGGERED")
551        pass
552
553    def actionLatex(self):
554        """
555        """
556        print("actionLatex TRIGGERED")
557        pass
558
559    #============ VIEW =================
560    def actionShow_Grid_Window(self):
561        """
562        """
[d4dac80]563        self.showBatchOutput(None)
564
565    def showBatchOutput(self, output_data):
566        """
567        Display/redisplay the batch fit viewer
568        """
569        if self.grid_window is None:
570            self.grid_window = BatchOutputPanel(parent=self, output_data=output_data)
571            subwindow = self._workspace.workspace.addSubWindow(self.grid_window)
572
573            #self.grid_window = BatchOutputPanel(parent=self, output_data=output_data)
574            self.grid_window.show()
575            return
576        if output_data:
577            self.grid_window.addFitResults(output_data)
578        self.grid_window.show()
579        if self.grid_window.windowState() == Qt.WindowMinimized:
580            self.grid_window.setWindowState(Qt.WindowActive)
[f721030]581
582    def actionHide_Toolbar(self):
583        """
[e540cd2]584        Toggle toolbar vsibility
[f721030]585        """
[e540cd2]586        if self._workspace.toolBar.isVisible():
587            self._workspace.actionHide_Toolbar.setText("Show Toolbar")
588            self._workspace.toolBar.setVisible(False)
589        else:
590            self._workspace.actionHide_Toolbar.setText("Hide Toolbar")
591            self._workspace.toolBar.setVisible(True)
[f721030]592        pass
593
594    def actionStartup_Settings(self):
595        """
596        """
597        print("actionStartup_Settings TRIGGERED")
598        pass
599
600    def actionCategry_Manager(self):
601        """
602        """
603        print("actionCategry_Manager TRIGGERED")
604        pass
605
606    #============ TOOLS =================
607    def actionData_Operation(self):
608        """
609        """
[f0bb711]610        self.communicate.sendDataToPanelSignal.emit(self._data_manager.get_all_data())
[d5c5d3d]611
612        self.DataOperation.show()
[f721030]613
614    def actionSLD_Calculator(self):
615        """
616        """
[1d85b5e]617        self.SLDCalculator.show()
[f721030]618
619    def actionDensity_Volume_Calculator(self):
620        """
621        """
[1d85b5e]622        self.DVCalculator.show()
[f721030]623
[363fbfa]624    def actionKiessig_Calculator(self):
625        """
626        """
627        self.KIESSIGCalculator.show()
628
[f721030]629    def actionSlit_Size_Calculator(self):
630        """
631        """
[a8ec5b1]632        self.SlitSizeCalculator.show()
[f721030]633
634    def actionSAS_Resolution_Estimator(self):
635        """
636        """
[fa05c6c1]637        try:
638            self.ResolutionCalculator.show()
639        except Exception as ex:
640            logging.error(str(ex))
641            return
[f721030]642
643    def actionGeneric_Scattering_Calculator(self):
644        """
645        """
[fa05c6c1]646        try:
647            self.GENSASCalculator.show()
648        except Exception as ex:
649            logging.error(str(ex))
650            return
[f721030]651
652    def actionPython_Shell_Editor(self):
653        """
[1af348e]654        Display the Jupyter console as a docked widget.
[f721030]655        """
[fef38e8]656        # Import moved here for startup performance reasons
657        from sas.qtgui.Utilities.IPythonWidget import IPythonWidget
[1af348e]658        terminal = IPythonWidget()
659
660        # Add the console window as another docked widget
[4992ff2]661        self.ipDockWidget = QDockWidget("IPython", self._workspace)
[1af348e]662        self.ipDockWidget.setObjectName("IPythonDockWidget")
663        self.ipDockWidget.setWidget(terminal)
[fbfc488]664        self._workspace.addDockWidget(Qt.RightDockWidgetArea, self.ipDockWidget)
[f721030]665
666    def actionImage_Viewer(self):
667        """
668        """
669        print("actionImage_Viewer TRIGGERED")
670        pass
671
672    #============ FITTING =================
673    def actionNew_Fit_Page(self):
674        """
[60af928]675        Add a new, empty Fit page in the fitting perspective.
[f721030]676        """
[60af928]677        # Make sure the perspective is correct
678        per = self.perspective()
679        if not isinstance(per, FittingWindow):
680            return
681        per.addFit(None)
[f721030]682
683    def actionConstrained_Fit(self):
684        """
[676f137]685        Add a new Constrained and Simult. Fit page in the fitting perspective.
[f721030]686        """
[676f137]687        per = self.perspective()
688        if not isinstance(per, FittingWindow):
689            return
690        per.addConstraintTab()
[f721030]691
692    def actionCombine_Batch_Fit(self):
693        """
694        """
695        print("actionCombine_Batch_Fit TRIGGERED")
696        pass
697
698    def actionFit_Options(self):
699        """
700        """
[2d0e0c1]701        if getattr(self._current_perspective, "fit_options_widget"):
702            self._current_perspective.fit_options_widget.show()
[f721030]703        pass
704
[06ce180]705    def actionGPU_Options(self):
706        """
[9863343]707        Load the OpenCL selection dialog if the fitting perspective is active
[06ce180]708        """
[9863343]709        if hasattr(self._current_perspective, "gpu_options_widget"):
[06ce180]710            self._current_perspective.gpu_options_widget.show()
711        pass
712
[f721030]713    def actionFit_Results(self):
714        """
715        """
716        print("actionFit_Results TRIGGERED")
717        pass
718
[3b3b40b]719    def actionAdd_Custom_Model(self):
720        """
721        """
722        self.model_editor = TabbedModelEditor(self)
723        self.model_editor.show()
724
[f721030]725    def actionEdit_Custom_Model(self):
726        """
727        """
[3b3b40b]728        self.model_editor = TabbedModelEditor(self, edit_only=True)
729        self.model_editor.show()
730
731    def actionManage_Custom_Models(self):
732        """
733        """
734        self.model_manager = PluginManager(self)
735        self.model_manager.show()
[f721030]736
[01ef3f7]737    def actionAddMult_Models(self):
738        """
739        """
[3b8cc00]740        # Add Simple Add/Multiply Editor
[01ef3f7]741        self.add_mult_editor = AddMultEditor(self)
742        self.add_mult_editor.show()
743
[f721030]744    #============ ANALYSIS =================
745    def actionFitting(self):
746        """
[9e54199]747        Change to the Fitting perspective
[f721030]748        """
[9e54199]749        self.perspectiveChanged("Fitting")
[8ac3551]750        # Notify other widgets
751        self.filesWidget.onAnalysisUpdate("Fitting")
[f721030]752
753    def actionInversion(self):
754        """
[9e54199]755        Change to the Inversion perspective
[f721030]756        """
[d4881f6a]757        self.perspectiveChanged("Inversion")
[8ac3551]758        self.filesWidget.onAnalysisUpdate("Inversion")
[f721030]759
760    def actionInvariant(self):
761        """
[9e54199]762        Change to the Invariant perspective
[f721030]763        """
[9e54199]764        self.perspectiveChanged("Invariant")
[8ac3551]765        self.filesWidget.onAnalysisUpdate("Invariant")
766
767    def actionCorfunc(self):
768        """
769        Change to the Corfunc perspective
770        """
771        self.perspectiveChanged("Corfunc")
772        self.filesWidget.onAnalysisUpdate("Corfunc")
[f721030]773
774    #============ WINDOW =================
775    def actionCascade(self):
776        """
[e540cd2]777        Arranges all the child windows in a cascade pattern.
[f721030]778        """
[2b39fea]779        self._workspace.workspace.cascadeSubWindows()
[f721030]780
[e540cd2]781    def actionTile(self):
[f721030]782        """
[e540cd2]783        Tile workspace windows
[f721030]784        """
[2b39fea]785        self._workspace.workspace.tileSubWindows()
[f721030]786
787    def actionArrange_Icons(self):
788        """
[e540cd2]789        Arranges all iconified windows at the bottom of the workspace
[f721030]790        """
[e540cd2]791        self._workspace.workspace.arrangeIcons()
[f721030]792
793    def actionNext(self):
794        """
[e540cd2]795        Gives the input focus to the next window in the list of child windows.
[f721030]796        """
[2b39fea]797        self._workspace.workspace.activateNextSubWindow()
[f721030]798
799    def actionPrevious(self):
800        """
[e540cd2]801        Gives the input focus to the previous window in the list of child windows.
[f721030]802        """
[2b39fea]803        self._workspace.workspace.activatePreviousSubWindow()
[f721030]804
805    #============ HELP =================
806    def actionDocumentation(self):
807        """
[9e426c1]808        Display the documentation
809
810        TODO: use QNetworkAccessManager to assure _helpLocation is valid
[f721030]811        """
[fe76fba]812        helpfile = "/index.html"
[aed0532]813        self.showHelp(helpfile)
[f721030]814
815    def actionTutorial(self):
816        """
[9e426c1]817        Open the tutorial PDF file with default PDF renderer
[f721030]818        """
[9e426c1]819        # Not terribly safe here. Shell injection warning.
820        # isfile() helps but this probably needs a better solution.
821        if os.path.isfile(self._tutorialLocation):
822            result = subprocess.Popen([self._tutorialLocation], shell=True)
[f721030]823
824    def actionAcknowledge(self):
825        """
[9e426c1]826        Open the Acknowledgements widget
[f721030]827        """
[9e426c1]828        self.ackWidget.show()
[f721030]829
830    def actionAbout(self):
831        """
[9e426c1]832        Open the About box
[f721030]833        """
[f82ab8c]834        # Update the about box with current version and stuff
835
836        # TODO: proper sizing
837        self.aboutWidget.show()
[f721030]838
839    def actionCheck_for_update(self):
840        """
[9e426c1]841        Menu Help/Check for Update
[f721030]842        """
[9e426c1]843        self.checkUpdate()
844
[cbcdd2c]845    def updateTheoryFromPerspective(self, index):
846        """
847        Catch the theory update signal from a perspective
848        Send the request to the DataExplorer for updating the theory model.
849        """
850        self.filesWidget.updateTheoryFromPerspective(index)
851
[d5c5d3d]852    def updateModelFromDataOperationPanel(self, new_item, new_datalist_item):
853        """
854        :param new_item: item to be added to list of loaded files
855        :param new_datalist_item:
856        """
[4992ff2]857        if not isinstance(new_item, QStandardItem) or \
[d5c5d3d]858                not isinstance(new_datalist_item, dict):
859            msg = "Wrong data type returned from calculations."
[b3e8629]860            raise AttributeError(msg)
[d5c5d3d]861
862        self.filesWidget.model.appendRow(new_item)
863        self._data_manager.add_data(new_datalist_item)
864
[3b3b40b]865    def showPlotFromFilename(self, filename):
866        """
867        Pass the show plot request to the data explorer
868        """
869        if hasattr(self, "filesWidget"):
870            self.filesWidget.displayFile(filename=filename, is_data=True)
871
[d48cc19]872    def showPlot(self, plot):
873        """
874        Pass the show plot request to the data explorer
875        """
876        if hasattr(self, "filesWidget"):
877            self.filesWidget.displayData(plot)
[9e54199]878
879    def uncheckAllMenuItems(self, menuObject):
880        """
881        Uncheck all options in a given menu
882        """
883        menuObjects = menuObject.actions()
884
885        for menuItem in menuObjects:
886            menuItem.setChecked(False)
887
888    def checkAnalysisOption(self, analysisMenuOption):
889        """
890        Unchecks all the items in the analysis menu and checks the item passed
891        """
892        self.uncheckAllMenuItems(self._workspace.menuAnalysis)
893        analysisMenuOption.setChecked(True)
894
895    def clearPerspectiveMenubarOptions(self, perspective):
896        """
897        When closing a perspective, clears the menu bar
898        """
899        for menuItem in self._workspace.menuAnalysis.actions():
900            menuItem.setChecked(False)
901
902        if isinstance(self._current_perspective, Perspectives.PERSPECTIVES["Fitting"]):
903            self._workspace.menubar.removeAction(self._workspace.menuFitting.menuAction())
904
905    def setupPerspectiveMenubarOptions(self, perspective):
906        """
907        When setting a perspective, sets up the menu bar
908        """
909        if isinstance(perspective, Perspectives.PERSPECTIVES["Fitting"]):
910            self.checkAnalysisOption(self._workspace.actionFitting)
911            # Put the fitting menu back in
912            # This is a bit involved but it is needed to preserve the menu ordering
913            self._workspace.menubar.removeAction(self._workspace.menuWindow.menuAction())
914            self._workspace.menubar.removeAction(self._workspace.menuHelp.menuAction())
915            self._workspace.menubar.addAction(self._workspace.menuFitting.menuAction())
916            self._workspace.menubar.addAction(self._workspace.menuWindow.menuAction())
917            self._workspace.menubar.addAction(self._workspace.menuHelp.menuAction())
918        elif isinstance(perspective, Perspectives.PERSPECTIVES["Invariant"]):
919            self.checkAnalysisOption(self._workspace.actionInvariant)
[8ac3551]920        elif isinstance(perspective, Perspectives.PERSPECTIVES["Inversion"]):
921            self.checkAnalysisOption(self._workspace.actionInversion)
922        elif isinstance(perspective, Perspectives.PERSPECTIVES["Corfunc"]):
923            self.checkAnalysisOption(self._workspace.actionCorfunc)
Note: See TracBrowser for help on using the repository browser.