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

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

More functionality conversion

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