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

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

Merge branch 'ESS_GUI' into ESS_GUI_better_batch

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