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

ESS_GUIESS_GUI_DocsESS_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 aed0532 was aed0532, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

Updated references to help files

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