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

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

Code review changes

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