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

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

Minor stability fixes for some calculators

  • Property mode set to 100644
File size: 31.7 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.KIESSIGCalculator.show()
605
[f721030]606    def actionSlit_Size_Calculator(self):
607        """
608        """
[a8ec5b1]609        self.SlitSizeCalculator.show()
[f721030]610
611    def actionSAS_Resolution_Estimator(self):
612        """
613        """
[fa05c6c1]614        try:
615            self.ResolutionCalculator.show()
616        except Exception as ex:
617            logging.error(str(ex))
618            return
[f721030]619
620    def actionGeneric_Scattering_Calculator(self):
621        """
622        """
[fa05c6c1]623        try:
624            self.GENSASCalculator.show()
625        except Exception as ex:
626            logging.error(str(ex))
627            return
[f721030]628
629    def actionPython_Shell_Editor(self):
630        """
[1af348e]631        Display the Jupyter console as a docked widget.
[f721030]632        """
[fef38e8]633        # Import moved here for startup performance reasons
634        from sas.qtgui.Utilities.IPythonWidget import IPythonWidget
[1af348e]635        terminal = IPythonWidget()
636
637        # Add the console window as another docked widget
[4992ff2]638        self.ipDockWidget = QDockWidget("IPython", self._workspace)
[1af348e]639        self.ipDockWidget.setObjectName("IPythonDockWidget")
640        self.ipDockWidget.setWidget(terminal)
[fbfc488]641        self._workspace.addDockWidget(Qt.RightDockWidgetArea, self.ipDockWidget)
[f721030]642
643    def actionImage_Viewer(self):
644        """
645        """
646        print("actionImage_Viewer TRIGGERED")
647        pass
648
649    #============ FITTING =================
650    def actionNew_Fit_Page(self):
651        """
[60af928]652        Add a new, empty Fit page in the fitting perspective.
[f721030]653        """
[60af928]654        # Make sure the perspective is correct
655        per = self.perspective()
656        if not isinstance(per, FittingWindow):
657            return
658        per.addFit(None)
[f721030]659
660    def actionConstrained_Fit(self):
661        """
[676f137]662        Add a new Constrained and Simult. Fit page in the fitting perspective.
[f721030]663        """
[676f137]664        per = self.perspective()
665        if not isinstance(per, FittingWindow):
666            return
667        per.addConstraintTab()
[f721030]668
669    def actionCombine_Batch_Fit(self):
670        """
671        """
672        print("actionCombine_Batch_Fit TRIGGERED")
673        pass
674
675    def actionFit_Options(self):
676        """
677        """
[2d0e0c1]678        if getattr(self._current_perspective, "fit_options_widget"):
679            self._current_perspective.fit_options_widget.show()
[f721030]680        pass
681
[06ce180]682    def actionGPU_Options(self):
683        """
[9863343]684        Load the OpenCL selection dialog if the fitting perspective is active
[06ce180]685        """
[9863343]686        if hasattr(self._current_perspective, "gpu_options_widget"):
[06ce180]687            self._current_perspective.gpu_options_widget.show()
688        pass
689
[f721030]690    def actionFit_Results(self):
691        """
692        """
693        print("actionFit_Results TRIGGERED")
694        pass
695
[3b3b40b]696    def actionAdd_Custom_Model(self):
697        """
698        """
699        self.model_editor = TabbedModelEditor(self)
700        self.model_editor.show()
701
[f721030]702    def actionEdit_Custom_Model(self):
703        """
704        """
[3b3b40b]705        self.model_editor = TabbedModelEditor(self, edit_only=True)
706        self.model_editor.show()
707
708    def actionManage_Custom_Models(self):
709        """
710        """
711        self.model_manager = PluginManager(self)
712        self.model_manager.show()
[f721030]713
[01ef3f7]714    def actionAddMult_Models(self):
715        """
716        """
[3b8cc00]717        # Add Simple Add/Multiply Editor
[01ef3f7]718        self.add_mult_editor = AddMultEditor(self)
719        self.add_mult_editor.show()
720
[f721030]721    #============ ANALYSIS =================
722    def actionFitting(self):
723        """
[9e54199]724        Change to the Fitting perspective
[f721030]725        """
[9e54199]726        self.perspectiveChanged("Fitting")
[8ac3551]727        # Notify other widgets
728        self.filesWidget.onAnalysisUpdate("Fitting")
[f721030]729
730    def actionInversion(self):
731        """
[9e54199]732        Change to the Inversion perspective
[f721030]733        """
[d4881f6a]734        self.perspectiveChanged("Inversion")
[8ac3551]735        self.filesWidget.onAnalysisUpdate("Inversion")
[f721030]736
737    def actionInvariant(self):
738        """
[9e54199]739        Change to the Invariant perspective
[f721030]740        """
[9e54199]741        self.perspectiveChanged("Invariant")
[8ac3551]742        self.filesWidget.onAnalysisUpdate("Invariant")
743
744    def actionCorfunc(self):
745        """
746        Change to the Corfunc perspective
747        """
748        self.perspectiveChanged("Corfunc")
749        self.filesWidget.onAnalysisUpdate("Corfunc")
[f721030]750
751    #============ WINDOW =================
752    def actionCascade(self):
753        """
[e540cd2]754        Arranges all the child windows in a cascade pattern.
[f721030]755        """
[e540cd2]756        self._workspace.workspace.cascade()
[f721030]757
[e540cd2]758    def actionTile(self):
[f721030]759        """
[e540cd2]760        Tile workspace windows
[f721030]761        """
[e540cd2]762        self._workspace.workspace.tile()
[f721030]763
764    def actionArrange_Icons(self):
765        """
[e540cd2]766        Arranges all iconified windows at the bottom of the workspace
[f721030]767        """
[e540cd2]768        self._workspace.workspace.arrangeIcons()
[f721030]769
770    def actionNext(self):
771        """
[e540cd2]772        Gives the input focus to the next window in the list of child windows.
[f721030]773        """
[e540cd2]774        self._workspace.workspace.activateNextWindow()
[f721030]775
776    def actionPrevious(self):
777        """
[e540cd2]778        Gives the input focus to the previous window in the list of child windows.
[f721030]779        """
[e540cd2]780        self._workspace.workspace.activatePreviousWindow()
[f721030]781
782    #============ HELP =================
783    def actionDocumentation(self):
784        """
[9e426c1]785        Display the documentation
786
787        TODO: use QNetworkAccessManager to assure _helpLocation is valid
[f721030]788        """
[aed0532]789        helpfile = "index.html"
790        self.showHelp(helpfile)
[f721030]791
792    def actionTutorial(self):
793        """
[9e426c1]794        Open the tutorial PDF file with default PDF renderer
[f721030]795        """
[9e426c1]796        # Not terribly safe here. Shell injection warning.
797        # isfile() helps but this probably needs a better solution.
798        if os.path.isfile(self._tutorialLocation):
799            result = subprocess.Popen([self._tutorialLocation], shell=True)
[f721030]800
801    def actionAcknowledge(self):
802        """
[9e426c1]803        Open the Acknowledgements widget
[f721030]804        """
[9e426c1]805        self.ackWidget.show()
[f721030]806
807    def actionAbout(self):
808        """
[9e426c1]809        Open the About box
[f721030]810        """
[f82ab8c]811        # Update the about box with current version and stuff
812
813        # TODO: proper sizing
814        self.aboutWidget.show()
[f721030]815
816    def actionCheck_for_update(self):
817        """
[9e426c1]818        Menu Help/Check for Update
[f721030]819        """
[9e426c1]820        self.checkUpdate()
821
[cbcdd2c]822    def updateTheoryFromPerspective(self, index):
823        """
824        Catch the theory update signal from a perspective
825        Send the request to the DataExplorer for updating the theory model.
826        """
827        self.filesWidget.updateTheoryFromPerspective(index)
828
[d5c5d3d]829    def updateModelFromDataOperationPanel(self, new_item, new_datalist_item):
830        """
831        :param new_item: item to be added to list of loaded files
832        :param new_datalist_item:
833        """
[4992ff2]834        if not isinstance(new_item, QStandardItem) or \
[d5c5d3d]835                not isinstance(new_datalist_item, dict):
836            msg = "Wrong data type returned from calculations."
[b3e8629]837            raise AttributeError(msg)
[d5c5d3d]838
839        self.filesWidget.model.appendRow(new_item)
840        self._data_manager.add_data(new_datalist_item)
841
[3b3b40b]842    def showPlotFromFilename(self, filename):
843        """
844        Pass the show plot request to the data explorer
845        """
846        if hasattr(self, "filesWidget"):
847            self.filesWidget.displayFile(filename=filename, is_data=True)
848
[d48cc19]849    def showPlot(self, plot):
850        """
851        Pass the show plot request to the data explorer
852        """
853        if hasattr(self, "filesWidget"):
854            self.filesWidget.displayData(plot)
[9e54199]855
856    def uncheckAllMenuItems(self, menuObject):
857        """
858        Uncheck all options in a given menu
859        """
860        menuObjects = menuObject.actions()
861
862        for menuItem in menuObjects:
863            menuItem.setChecked(False)
864
865    def checkAnalysisOption(self, analysisMenuOption):
866        """
867        Unchecks all the items in the analysis menu and checks the item passed
868        """
869        self.uncheckAllMenuItems(self._workspace.menuAnalysis)
870        analysisMenuOption.setChecked(True)
871
872    def clearPerspectiveMenubarOptions(self, perspective):
873        """
874        When closing a perspective, clears the menu bar
875        """
876        for menuItem in self._workspace.menuAnalysis.actions():
877            menuItem.setChecked(False)
878
879        if isinstance(self._current_perspective, Perspectives.PERSPECTIVES["Fitting"]):
880            self._workspace.menubar.removeAction(self._workspace.menuFitting.menuAction())
881
882    def setupPerspectiveMenubarOptions(self, perspective):
883        """
884        When setting a perspective, sets up the menu bar
885        """
886        if isinstance(perspective, Perspectives.PERSPECTIVES["Fitting"]):
887            self.checkAnalysisOption(self._workspace.actionFitting)
888            # Put the fitting menu back in
889            # This is a bit involved but it is needed to preserve the menu ordering
890            self._workspace.menubar.removeAction(self._workspace.menuWindow.menuAction())
891            self._workspace.menubar.removeAction(self._workspace.menuHelp.menuAction())
892            self._workspace.menubar.addAction(self._workspace.menuFitting.menuAction())
893            self._workspace.menubar.addAction(self._workspace.menuWindow.menuAction())
894            self._workspace.menubar.addAction(self._workspace.menuHelp.menuAction())
895        elif isinstance(perspective, Perspectives.PERSPECTIVES["Invariant"]):
896            self.checkAnalysisOption(self._workspace.actionInvariant)
[8ac3551]897        elif isinstance(perspective, Perspectives.PERSPECTIVES["Inversion"]):
898            self.checkAnalysisOption(self._workspace.actionInversion)
899        elif isinstance(perspective, Perspectives.PERSPECTIVES["Corfunc"]):
900            self.checkAnalysisOption(self._workspace.actionCorfunc)
Note: See TracBrowser for help on using the repository browser.