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

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

Updated references to help files

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