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

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

Fixed Mdi area and widget resizing issue(s)

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