source: sasview/src/sas/qtgui/GuiManager.py @ 481ff26

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 481ff26 was 481ff26, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 8 years ago

Modified Data Explorer slightly

  • Property mode set to 100755
File size: 18.3 KB
Line 
1import sys
2import subprocess
3import logging
4import json
5import webbrowser
6
7from PyQt4 import QtCore
8from PyQt4 import QtGui
9from PyQt4 import QtWebKit
10
11from twisted.internet import reactor
12
13# General SAS imports
14from sas.sasgui.guiframe.data_manager import DataManager
15from sas.sasgui.guiframe.proxy import Connection
16
17import LocalConfig
18from GuiUtils import *
19from UI.AcknowledgementsUI import Acknowledgements
20from AboutBox import AboutBox
21
22# Perspectives
23from Perspectives.Invariant.InvariantPerspective import InvariantWindow
24from DataExplorer import DataExplorerWindow
25from WelcomePanel import WelcomePanel
26
27class GuiManager(object):
28    """
29    Main SasView window functionality
30    """
31    HELP_DIRECTORY_LOCATION = "html"
32
33    def __init__(self, mainWindow=None, reactor=None, parent=None):
34        """
35        """
36
37        self._workspace = mainWindow
38        self._parent = parent
39
40        # Reactor passed from above
41        self.setReactor(reactor)
42
43        # Add signal callbacks
44        self.addCallbacks()
45
46        # Create the data manager
47        # TODO: pull out all required methods from DataManager and reimplement
48        self._data_manager = DataManager()
49
50        # Create action triggers
51        self.addTriggers()
52
53        # Populate menus with dynamic data
54        #
55        # Analysis/Perspectives - potentially
56        # Window/current windows
57        #
58        # Widgets
59        #
60        # Add FileDialog widget as docked
61        self.filesWidget = DataExplorerWindow(parent, self)
62        #flags = (QtCore.Qt.Window | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowMinimizeButtonHint)
63        flags = (QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint |
64                 QtCore.Qt.WindowMinMaxButtonsHint)
65
66        self.dockedFilesWidget = QtGui.QDockWidget("Data explorer", self._workspace, flags=flags)
67        self.dockedFilesWidget.setWidget(self.filesWidget)
68        self._workspace.addDockWidget(QtCore.Qt.DockWidgetArea(1), self.dockedFilesWidget)
69
70        self.ackWidget = Acknowledgements()
71        self.aboutWidget = AboutBox()
72
73        # Disable the close button (?)
74
75        # Show the Welcome panel
76        self.welcomePanel = WelcomePanel()
77        self._workspace.workspace.addWindow(self.welcomePanel)
78
79        # Current help file
80        self._helpView = QtWebKit.QWebView()
81        # Needs URL like path, so no path.join() here
82        self._helpLocation = self.HELP_DIRECTORY_LOCATION + "/index.html"
83
84        # Current tutorial location
85        self._tutorialLocation = os.path.join(self.HELP_DIRECTORY_LOCATION,
86                                              "_downloads",
87                                              "Tutorial.pdf")
88
89        #==========================================================
90        # TEMP PROTOTYPE
91        # Add InvariantWindow to the workspace.
92        self.invariantWidget = InvariantWindow(self)
93        self._workspace.workspace.addWindow(self.invariantWidget)
94
95        # Default perspective
96        self._current_perspective = self.invariantWidget
97
98    def fileRead(self, data):
99        """
100        Callback for fileDataReceivedSignal
101        """
102        pass
103
104    def updatePerspective(self, data):
105        """
106        """
107        assert isinstance(data, list)
108        if self._current_perspective is not None:
109            self._current_perspective.setData(data.values())
110        else:
111            msg = "No perspective is currently active."
112            logging.info(msg)
113
114
115    def communicator(self):
116        """
117        """
118        return self.communicate
119
120    def reactor(self):
121        """
122        """
123        return self._reactor
124
125    def setReactor(self, reactor):
126        """
127        """
128        self._reactor = reactor
129
130    def perspective(self):
131        """
132        """
133        return self._current_perspective
134
135    def updateStatusBar(self, text):
136        """
137        """
138        self._workspace.statusbar.showMessage(text)
139
140    def createGuiData(self, item, p_file=None):
141        """
142        Access the Data1D -> plottable Data1D conversion
143        """
144        return self._data_manager.create_gui_data(item, p_file)
145
146    def setData(self, data):
147        """
148        Sends data to current perspective
149        """
150        if self._current_perspective is not None:
151            self._current_perspective.setData(data.values())
152        else:
153            msg = "Guiframe does not have a current perspective"
154            logging.info(msg)
155
156    def quitApplication(self):
157        """
158        Close the reactor and exit nicely.
159        """
160        # Display confirmation messagebox
161        quit_msg = "Are you sure you want to exit the application?"
162        reply = QtGui.QMessageBox.question(
163            self._parent,
164            'Warning',
165            quit_msg,
166            QtGui.QMessageBox.Yes,
167            QtGui.QMessageBox.No)
168
169        if reply == QtGui.QMessageBox.No:
170            return
171
172        # Exit if yes
173        reactor.callFromThread(reactor.stop)
174
175    def checkUpdate(self):
176        """
177        Check with the deployment server whether a new version
178        of the application is available.
179        A thread is started for the connecting with the server. The thread calls
180        a call-back method when the current version number has been obtained.
181        """
182        version_info = {"version": "0.0.0"}
183        c = Connection(LocalConfig.__update_URL__, LocalConfig.UPDATE_TIMEOUT)
184        response = c.connect()
185        if response is not None:
186            try:
187                content = response.read().strip()
188                logging.info("Connected to www.sasview.org. Latest version: %s"
189                             % (content))
190                version_info = json.loads(content)
191            except:
192                logging.info("Failed to connect to www.sasview.org")
193        self.processVersion(version_info)
194
195    def processVersion(self, version_info):
196        """
197        Call-back method for the process of checking for updates.
198        This methods is called by a VersionThread object once the current
199        version number has been obtained. If the check is being done in the
200        background, the user will not be notified unless there's an update.
201
202        :param version: version string
203        """
204        try:
205            version = version_info["version"]
206            if version == "0.0.0":
207                msg = "Could not connect to the application server."
208                msg += " Please try again later."
209                #self.SetStatusText(msg)
210                self.communicate.statusBarUpdateSignal.emit(msg)
211
212            elif cmp(version, LocalConfig.__version__) > 0:
213                msg = "Version %s is available! " % str(version)
214                if "download_url" in version_info:
215                    webbrowser.open(version_info["download_url"])
216                else:
217                    webbrowser.open(LocalConfig.__download_page__)
218                self.communicate.statusBarUpdateSignal.emit(msg)
219            else:
220                msg = "You have the latest version"
221                msg += " of %s" % str(LocalConfig.__appname__)
222                self.communicate.statusBarUpdateSignal.emit(msg)
223        except:
224            msg = "guiframe: could not get latest application"
225            msg += " version number\n  %s" % sys.exc_value
226            logging.error(msg)
227            msg = "Could not connect to the application server."
228            msg += " Please try again later."
229            self.communicate.statusBarUpdateSignal.emit(msg)
230
231    def addCallbacks(self):
232        """
233        Method defining all signal connections for the gui manager
234        """
235        self.communicate = Communicate()
236        self.communicate.fileDataReceivedSignal.connect(self.fileRead)
237        self.communicate.statusBarUpdateSignal.connect(self.updateStatusBar)
238        self.communicate.updatePerspectiveWithDataSignal.connect(self.updatePerspective)
239
240    def addTriggers(self):
241        """
242        Trigger definitions for all menu/toolbar actions.
243        """
244        # File
245        self._workspace.actionLoadData.triggered.connect(self.actionLoadData)
246        self._workspace.actionLoad_Data_Folder.triggered.connect(self.actionLoad_Data_Folder)
247        self._workspace.actionOpen_Project.triggered.connect(self.actionOpen_Project)
248        self._workspace.actionOpen_Analysis.triggered.connect(self.actionOpen_Analysis)
249        self._workspace.actionSave.triggered.connect(self.actionSave)
250        self._workspace.actionSave_Analysis.triggered.connect(self.actionSave_Analysis)
251        self._workspace.actionQuit.triggered.connect(self.actionQuit)
252        # Edit
253        self._workspace.actionUndo.triggered.connect(self.actionUndo)
254        self._workspace.actionRedo.triggered.connect(self.actionRedo)
255        self._workspace.actionCopy.triggered.connect(self.actionCopy)
256        self._workspace.actionPaste.triggered.connect(self.actionPaste)
257        self._workspace.actionReport.triggered.connect(self.actionReport)
258        self._workspace.actionReset.triggered.connect(self.actionReset)
259        self._workspace.actionExcel.triggered.connect(self.actionExcel)
260        self._workspace.actionLatex.triggered.connect(self.actionLatex)
261
262        # View
263        self._workspace.actionShow_Grid_Window.triggered.connect(self.actionShow_Grid_Window)
264        self._workspace.actionHide_Toolbar.triggered.connect(self.actionHide_Toolbar)
265        self._workspace.actionStartup_Settings.triggered.connect(self.actionStartup_Settings)
266        self._workspace.actionCategry_Manager.triggered.connect(self.actionCategry_Manager)
267        # Tools
268        self._workspace.actionData_Operation.triggered.connect(self.actionData_Operation)
269        self._workspace.actionSLD_Calculator.triggered.connect(self.actionSLD_Calculator)
270        self._workspace.actionDensity_Volume_Calculator.triggered.connect(self.actionDensity_Volume_Calculator)
271        self._workspace.actionSlit_Size_Calculator.triggered.connect(self.actionSlit_Size_Calculator)
272        self._workspace.actionSAS_Resolution_Estimator.triggered.connect(self.actionSAS_Resolution_Estimator)
273        self._workspace.actionGeneric_Scattering_Calculator.triggered.connect(self.actionGeneric_Scattering_Calculator)
274        self._workspace.actionPython_Shell_Editor.triggered.connect(self.actionPython_Shell_Editor)
275        self._workspace.actionImage_Viewer.triggered.connect(self.actionImage_Viewer)
276        # Fitting
277        self._workspace.actionNew_Fit_Page.triggered.connect(self.actionNew_Fit_Page)
278        self._workspace.actionConstrained_Fit.triggered.connect(self.actionConstrained_Fit)
279        self._workspace.actionCombine_Batch_Fit.triggered.connect(self.actionCombine_Batch_Fit)
280        self._workspace.actionFit_Options.triggered.connect(self.actionFit_Options)
281        self._workspace.actionFit_Results.triggered.connect(self.actionFit_Results)
282        self._workspace.actionChain_Fitting.triggered.connect(self.actionChain_Fitting)
283        self._workspace.actionEdit_Custom_Model.triggered.connect(self.actionEdit_Custom_Model)
284        # Window
285        self._workspace.actionCascade.triggered.connect(self.actionCascade)
286        self._workspace.actionTile_Horizontally.triggered.connect(self.actionTile_Horizontally)
287        self._workspace.actionTile_Vertically.triggered.connect(self.actionTile_Vertically)
288        self._workspace.actionArrange_Icons.triggered.connect(self.actionArrange_Icons)
289        self._workspace.actionNext.triggered.connect(self.actionNext)
290        self._workspace.actionPrevious.triggered.connect(self.actionPrevious)
291        # Analysis
292        self._workspace.actionFitting.triggered.connect(self.actionFitting)
293        self._workspace.actionInversion.triggered.connect(self.actionInversion)
294        self._workspace.actionInvariant.triggered.connect(self.actionInvariant)
295        # Help
296        self._workspace.actionDocumentation.triggered.connect(self.actionDocumentation)
297        self._workspace.actionTutorial.triggered.connect(self.actionTutorial)
298        self._workspace.actionAcknowledge.triggered.connect(self.actionAcknowledge)
299        self._workspace.actionAbout.triggered.connect(self.actionAbout)
300        self._workspace.actionCheck_for_update.triggered.connect(self.actionCheck_for_update)
301
302    #============ FILE =================
303    def actionLoadData(self):
304        """
305        Menu File/Load Data File(s)
306        """
307        self.filesWidget.loadFile()
308
309    def actionLoad_Data_Folder(self):
310        """
311        Menu File/Load Data Folder
312        """
313        self.filesWidget.loadFolder()
314
315    def actionOpen_Project(self):
316        """
317        """
318        print("actionOpen_Project TRIGGERED")
319        pass
320
321    def actionOpen_Analysis(self):
322        """
323        """
324        print("actionOpen_Analysis TRIGGERED")
325        pass
326
327    def actionSave(self):
328        """
329        """
330        print("actionSave TRIGGERED")
331        pass
332
333    def actionSave_Analysis(self):
334        """
335        """
336        print("actionSave_Analysis TRIGGERED")
337
338        pass
339
340    def actionQuit(self):
341        """
342        Close the reactor, exit the application.
343        """
344        self.quitApplication()
345
346    #============ EDIT =================
347    def actionUndo(self):
348        """
349        """
350        print("actionUndo TRIGGERED")
351        pass
352
353    def actionRedo(self):
354        """
355        """
356        print("actionRedo TRIGGERED")
357        pass
358
359    def actionCopy(self):
360        """
361        """
362        print("actionCopy TRIGGERED")
363        pass
364
365    def actionPaste(self):
366        """
367        """
368        print("actionPaste TRIGGERED")
369        pass
370
371    def actionReport(self):
372        """
373        """
374        print("actionReport TRIGGERED")
375        pass
376
377    def actionReset(self):
378        """
379        """
380        print("actionReset TRIGGERED")
381        pass
382
383    def actionExcel(self):
384        """
385        """
386        print("actionExcel TRIGGERED")
387        pass
388
389    def actionLatex(self):
390        """
391        """
392        print("actionLatex TRIGGERED")
393        pass
394
395    #============ VIEW =================
396    def actionShow_Grid_Window(self):
397        """
398        """
399        print("actionShow_Grid_Window TRIGGERED")
400        pass
401
402    def actionHide_Toolbar(self):
403        """
404        """
405        print("actionHide_Toolbar TRIGGERED")
406        pass
407
408    def actionStartup_Settings(self):
409        """
410        """
411        print("actionStartup_Settings TRIGGERED")
412        pass
413
414    def actionCategry_Manager(self):
415        """
416        """
417        print("actionCategry_Manager TRIGGERED")
418        pass
419
420    #============ TOOLS =================
421    def actionData_Operation(self):
422        """
423        """
424        print("actionData_Operation TRIGGERED")
425        pass
426
427    def actionSLD_Calculator(self):
428        """
429        """
430        print("actionSLD_Calculator TRIGGERED")
431        pass
432
433    def actionDensity_Volume_Calculator(self):
434        """
435        """
436        print("actionDensity_Volume_Calculator TRIGGERED")
437        pass
438
439    def actionSlit_Size_Calculator(self):
440        """
441        """
442        print("actionSlit_Size_Calculator TRIGGERED")
443        pass
444
445    def actionSAS_Resolution_Estimator(self):
446        """
447        """
448        print("actionSAS_Resolution_Estimator TRIGGERED")
449        pass
450
451    def actionGeneric_Scattering_Calculator(self):
452        """
453        """
454        print("actionGeneric_Scattering_Calculator TRIGGERED")
455        pass
456
457    def actionPython_Shell_Editor(self):
458        """
459        """
460        print("actionPython_Shell_Editor TRIGGERED")
461        pass
462
463    def actionImage_Viewer(self):
464        """
465        """
466        print("actionImage_Viewer TRIGGERED")
467        pass
468
469    #============ FITTING =================
470    def actionNew_Fit_Page(self):
471        """
472        """
473        print("actionNew_Fit_Page TRIGGERED")
474        pass
475
476    def actionConstrained_Fit(self):
477        """
478        """
479        print("actionConstrained_Fit TRIGGERED")
480        pass
481
482    def actionCombine_Batch_Fit(self):
483        """
484        """
485        print("actionCombine_Batch_Fit TRIGGERED")
486        pass
487
488    def actionFit_Options(self):
489        """
490        """
491        print("actionFit_Options TRIGGERED")
492        pass
493
494    def actionFit_Results(self):
495        """
496        """
497        print("actionFit_Results TRIGGERED")
498        pass
499
500    def actionChain_Fitting(self):
501        """
502        """
503        print("actionChain_Fitting TRIGGERED")
504        pass
505
506    def actionEdit_Custom_Model(self):
507        """
508        """
509        print("actionEdit_Custom_Model TRIGGERED")
510        pass
511
512    #============ ANALYSIS =================
513    def actionFitting(self):
514        """
515        """
516        print("actionFitting TRIGGERED")
517        pass
518
519    def actionInversion(self):
520        """
521        """
522        print("actionInversion TRIGGERED")
523        pass
524
525    def actionInvariant(self):
526        """
527        """
528        print("actionInvariant TRIGGERED")
529        pass
530
531    #============ WINDOW =================
532    def actionCascade(self):
533        """
534        """
535        print("actionCascade TRIGGERED")
536        pass
537
538    def actionTile_Horizontally(self):
539        """
540        """
541        print("actionTile_Horizontally TRIGGERED")
542        pass
543
544    def actionTile_Vertically(self):
545        """
546        """
547        print("actionTile_Vertically TRIGGERED")
548        pass
549
550    def actionArrange_Icons(self):
551        """
552        """
553        print("actionArrange_Icons TRIGGERED")
554        pass
555
556    def actionNext(self):
557        """
558        """
559        print("actionNext TRIGGERED")
560        pass
561
562    def actionPrevious(self):
563        """
564        """
565        print("actionPrevious TRIGGERED")
566        pass
567
568    #============ HELP =================
569    def actionDocumentation(self):
570        """
571        Display the documentation
572
573        TODO: use QNetworkAccessManager to assure _helpLocation is valid
574        """
575        self._helpView.load(QtCore.QUrl(self._helpLocation))
576        self._helpView.show()
577
578    def actionTutorial(self):
579        """
580        Open the tutorial PDF file with default PDF renderer
581        """
582        # Not terribly safe here. Shell injection warning.
583        # isfile() helps but this probably needs a better solution.
584        if os.path.isfile(self._tutorialLocation):
585            result = subprocess.Popen([self._tutorialLocation], shell=True)
586
587    def actionAcknowledge(self):
588        """
589        Open the Acknowledgements widget
590        """
591        self.ackWidget.show()
592
593    def actionAbout(self):
594        """
595        Open the About box
596        """
597        # Update the about box with current version and stuff
598
599        # TODO: proper sizing
600        self.aboutWidget.show()
601
602    def actionCheck_for_update(self):
603        """
604        Menu Help/Check for Update
605        """
606        self.checkUpdate()
607
608        pass
609
Note: See TracBrowser for help on using the repository browser.