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

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

More Qt5 related fixes

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