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

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

Initial implementation of Adam Washington's Corfunc perspective.
Converted to py3/Qt5.

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