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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since fa05c6c1 was fa05c6c1, checked in by Piotr Rozyczko <rozyczko@…>, 2 years ago

Minor stability fixes for some calculators

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