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

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 7d353af was 01cda57, checked in by celinedurniak <celine.durniak@…>, 7 years ago

Added new GUI for Q resolution estimator

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