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

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

Analysis menu properly interacting with the perspective combo.
Fixed menu enablement for Fitting.

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