source: sasview/src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py @ 0cd8612

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

output console + logging

  • Property mode set to 100755
File size: 23.5 KB
Line 
1# global
2import sys
3import os
4from PyQt4 import QtCore
5from PyQt4 import QtGui
6from PyQt4 import QtWebKit
7
8from twisted.internet import threads
9from twisted.internet import reactor
10
11# sas-global
12from sas.sascalc.invariant import invariant
13from sas.sasgui.guiframe.dataFitting import Data1D
14import GuiUtils
15
16# local
17from UI.TabbedInvariantUI import tabbedInvariantUI
18from InvariantDetails import DetailsDialog
19from Plotter import Plotter
20from InvariantUtils import WIDGETS
21
22# The minimum q-value to be used when extrapolating
23Q_MINIMUM = 1e-5
24# The maximum q-value to be used when extrapolating
25Q_MAXIMUM = 10
26# the ratio of maximum q value/(qmax of data) to plot the theory data
27Q_MAXIMUM_PLOT = 3
28
29
30class MyModel(object):
31    def __init__(self):
32        self._model = QtGui.QStandardItemModel(self)
33
34    def addItem(self, item):
35        item = QtGui.QStandardItem(str(item))
36        self._model.appendRow(item)
37
38class InvariantWindow(tabbedInvariantUI):
39    # The controller which is responsible for managing signal slots connections
40    # for the gui and providing an interface to the data model.
41    def __init__(self, manager=None, parent=None):
42        super(InvariantWindow, self).__init__(parent)
43        self.setWindowTitle("Invariant Perspective")
44
45        # initial input params
46        self._background = 0.0
47        self._scale = 1.0
48        self._contrast = 1.0
49        self._porod = 1.0
50        self._npoints_low = 10
51        self._npoints_high = 10
52        self._power_low = 4
53
54        self._manager = manager
55        self._reactor = self._manager.reactor()
56        self._model_item = QtGui.QStandardItem()
57
58        self._helpView = QtWebKit.QWebView()
59        self.detailsDialog = DetailsDialog(self)
60        self._plotter = Plotter(self)
61
62        self._low_extrapolate = False
63        self._low_guinier  = True
64        self._low_fit  = True
65        self._high_extrapolate = False
66        self._high_power_value  = False
67
68        self.communicate = GuiUtils.Communicate()
69
70        # Mask file selector
71        ###################################################
72        self._path = "cyl_400_20.txt"
73        from sas.sascalc.dataloader.loader import  Loader
74        loader = Loader()
75        try:
76            self._data = loader.load(self._path)
77        except:
78            raise
79        ###################################################
80
81        self.lineEdit_8.setText(str(Q_MINIMUM))
82        self.lineEdit_9.setText(str(Q_MAXIMUM))
83
84        # Let's choose the Standard Item Model.
85        self.model = QtGui.QStandardItemModel(self)
86
87        # Connect buttons to slots.
88        # Needs to be done early so default values propagate properly.
89        self.setupSlots()
90
91        # Set up the model.
92        self.setupModel()
93
94        # Set up the mapper
95        self.setupMapper()
96
97    def closeEvent(self, event):
98        """
99        Overwrite the default close method of QWidget
100        """
101        # No close on perspectives - one must always be active.
102        event.ignore()
103
104    def communicator(self):
105        """ Getter for the communicator """
106        return self.communicate
107
108    def updateFromModel(self):
109        """
110        update the globals based on the data in the model
111        """
112        self._background = float(self.model.item(WIDGETS.W_BACKGROUND).text())
113        self._contrast   = float(self.model.item(WIDGETS.W_CONTRAST).text())
114        self._scale      = float(self.model.item(WIDGETS.W_SCALE).text())
115
116        # High extrapolate
117        self._low_extrapolate = ( str(self.model.item(WIDGETS.W_ENABLE_LOWQ).text()) == 'true')
118        self._low_points = float(self.model.item(WIDGETS.W_NPTS_LOWQ).text())
119        self._low_guinier  = ( str(self.model.item(WIDGETS.W_LOWQ_GUINIER).text()) == 'true')
120        self._low_fit  = ( str(self.model.item(WIDGETS.W_LOWQ_FIT).text()) == 'true')
121        self._low_power_value  = float(self.model.item(WIDGETS.W_LOWQ_POWER_VALUE).text())
122
123        # High extrapolating
124        self._high_extrapolate = ( str(self.model.item(WIDGETS.W_ENABLE_HIGHQ).text()) == 'true')
125        self._high_points  = float(self.model.item(WIDGETS.W_NPTS_HIGHQ).text())
126        self._high_fit  = ( str(self.model.item(WIDGETS.W_HIGHQ_FIT).text()) == 'true')
127        self._high_power_value  = float(self.model.item(WIDGETS.W_HIGHQ_POWER_VALUE).text())
128
129    def calculateInvariant(self):
130        """
131        Use twisted to thread the calculations away.
132        """
133        # Find out if extrapolation needs to be used.
134        extrapolation = None
135        if self._low_extrapolate  and not self._high_extrapolate:
136            extrapolation = "low"
137        elif not self._low_extrapolate  and self._high_extrapolate:
138            extrapolation = "high"
139        elif self._low_extrapolate and self._high_extrapolate:
140            extrapolation = "both"
141        try:
142            # modify the Calculate button to indicate background process
143            self.pushButton.setText("Calculating...")
144            self.pushButton.setEnabled(False)
145            self.style = self.pushButton.styleSheet()
146            self.pushButton.setStyleSheet("background-color: rgb(255, 255, 0); color: rgb(0, 0, 0)")
147            # Send the calculations to separate thread.
148            d = threads.deferToThread(self.calculateThread, extrapolation)
149            # Add deferred callback for call return
150            d.addCallback(self.plotResult)
151        except Exception as ex:
152            # Set the button back to available
153            self.pushButton.setEnabled(True)
154            self.pushButton.setText("Calculate")
155            self.pushButton.setStyleSheet(self.style)
156
157
158    def plotResult(self, model):
159        """
160        """
161        if self._low_extrapolate or self._high_extrapolate:
162            self._plotter.show()
163        self.model = model
164        self.mapper.toFirst()
165
166        # Set the button back to available
167        self.pushButton.setEnabled(True)
168        self.pushButton.setText("Calculate")
169        self.pushButton.setStyleSheet(self.style)
170
171        # Send the modified model item to DE for keeping in the model
172        self.communicate.updateModelFromPerspectiveSignal.emit(self._model_item)
173
174
175    def calculateThread(self, extrapolation):
176        """
177        Perform Invariant calculations.
178
179        TODO: Create a dictionary of results to be sent to DE on completion.
180        """
181        self.updateFromModel()
182
183        qstar_low = 0.0
184        qstar_low_err = 0.0
185        qstar_high = 0.0
186        qstar_high_err = 0.0
187        qstar_total = 0.0
188        qstar_total_low_err = 0.0
189
190        # Prepare the invariant object
191        inv = invariant.InvariantCalculator(data=self._data,
192                                            background = self._background,
193                                            scale = self._scale)
194
195        if self._low_extrapolate:
196            function_low = "power_law"
197            if self._low_guinier:
198                function_low = "guinier"
199            if self._low_fit:
200                self._low_power_value = None
201            inv.set_extrapolation(range="low",
202                                  npts=self._low_points,
203                                  function=function_low,
204                                  power=self._low_power_value)
205
206        if self._high_extrapolate:
207            function_low = "power_law"
208            inv.set_extrapolation(range="high",
209                                  npts=self._high_points,
210                                  function=function_low,
211                                  power=self._low_power_value)
212
213        #Compute invariant
214        # TODO: proper exception handling and logic -
215        # display info, update lineedits, don't run extrapolations etc.
216        calculation_failed = False
217        try:
218            qstar_total, qstar_total_error         = inv.get_qstar_with_error()
219        except Exception as ex:
220            calculation_failed = True
221            # Display relevant information
222            item = QtGui.QStandardItem("ERROR")
223            self.model.setItem(WIDGETS.W_INVARIANT, item)
224            item = QtGui.QStandardItem("ERROR")
225            self.model.setItem(WIDGETS.W_INVARIANT_ERR, item)
226        try:
227            volume_fraction, volume_fraction_error = \
228                inv.get_volume_fraction_with_error(self._contrast)
229        except Exception as ex:
230            calculation_failed = True
231            # Display relevant information
232            item = QtGui.QStandardItem("ERROR")
233            self.model.setItem(WIDGETS.W_VOLUME_FRACTION, item)
234            item = QtGui.QStandardItem("ERROR")
235            self.model.setItem(WIDGETS.W_VOLUME_FRACTION_ERR, item)
236        try:
237            surface, surface_error                 = \
238                inv.get_surface_with_error(self._contrast, self._porod)
239        except Exception as ex:
240            calculation_failed = True
241            # Display relevant information
242            item = QtGui.QStandardItem("ERROR")
243            self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE, item)
244            item = QtGui.QStandardItem("ERROR")
245            self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE_ERR, item)
246
247        if(calculation_failed):
248            # TODO: NOTIFY THE GUI MANAGER!!
249            self.mapper.toFirst()
250            return self.model
251
252        self._plotter.clean()
253        self._plotter.x_label("Q(A$^{-1}$)")
254        self._plotter.y_label("Intensity(cm$^{-1}$)")
255
256        if self._low_extrapolate:
257            # for presentation in InvariantDetails
258            qstar_low, qstar_low_err = inv.get_qstar_low()
259            extrapolated_data = inv.get_extra_data_low(self._low_points)
260            power_low = inv.get_extrapolation_power(range='low')
261
262            #inv.data = extrapolated_data
263            #qstar_total, qstar_total_error = inv.get_qstar_with_error()
264
265            # Plot the chart
266            title = "Low-Q extrapolation"
267            self._plotter.data(extrapolated_data)
268            self._plotter.title(title)
269            self._plotter.plot()
270
271            # Convert the data into plottable
272            extrapolated_data = self._manager.createGuiData(extrapolated_data)
273
274            # Add the plot to the model item
275            # variant_item = QtCore.QVariant(self._plotter)
276            variant_item = QtCore.QVariant(extrapolated_data)
277
278            # This needs to run in the main thread
279            reactor.callFromThread(GuiUtils.updateModelItem, self._model_item, variant_item, title)
280
281        if self._high_extrapolate:
282            # for presentation in InvariantDetails
283            qmax_plot = Q_MAXIMUM_PLOT * max(self._data.x)
284            if qmax_plot > Q_MAXIMUM:
285                qmax_plot = Q_MAXIMUM
286            qstar_high, qstar_high_err = inv.get_qstar_high()
287            power_high = inv.get_extrapolation_power(range='high')
288            high_out_data = inv.get_extra_data_high(q_end=qmax_plot, npts=500)
289
290            # Convert the data into plottable
291            high_out_data = self._manager.createGuiData(high_out_data)
292
293            # find how to add this plot to the existing plot for low_extrapolate
294            # Plot the chart
295            title = "High-Q extrapolation"
296            self._plotter.data(high_out_data)
297            self._plotter.title(title)
298            self._plotter.plot()
299
300            # Add the plot to the model item
301            # variant_item = QtCore.QVariant(self._plotter)
302            variant_item = QtCore.QVariant(high_out_data)
303            # This needs to run in the main thread
304            reactor.callFromThread(updateModelItem, self._model_item, variant_item, title)
305
306        item = QtGui.QStandardItem(str(float('%.5g'% volume_fraction)))
307        self.model.setItem(WIDGETS.W_VOLUME_FRACTION, item)
308        item = QtGui.QStandardItem(str(float('%.5g'% volume_fraction_error)))
309        self.model.setItem(WIDGETS.W_VOLUME_FRACTION_ERR, item)
310        item = QtGui.QStandardItem(str(float('%.5g'% surface)))
311        self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE, item)
312        item = QtGui.QStandardItem(str(float('%.5g'% surface_error)))
313        self.model.setItem(WIDGETS.W_SPECIFIC_SURFACE_ERR, item)
314        item = QtGui.QStandardItem(str(float('%.5g'% qstar_total)))
315        self.model.setItem(WIDGETS.W_INVARIANT, item)
316        item = QtGui.QStandardItem(str(float('%.5g'% qstar_total_error)))
317        self.model.setItem(WIDGETS.W_INVARIANT_ERR, item)
318
319        #item = QtGui.QStandardItem(str(float('%.5g'% qstar_total)))
320        #self.model.setItem(WIDGETS.D_TOTAL_QSTAR, item)
321        #item = QtGui.QStandardItem(str(float('%.5g'% qstar_total_err)))
322        #self.model.setItem(WIDGETS.D_TOTAL_QSTAR_ERR, item)
323        item = QtGui.QStandardItem(str(float('%.5g'% qstar_low)))
324        self.model.setItem(WIDGETS.D_LOW_QSTAR, item)
325        item = QtGui.QStandardItem(str(float('%.5g'% qstar_low_err)))
326        self.model.setItem(WIDGETS.D_LOW_QSTAR_ERR, item)
327        item = QtGui.QStandardItem(str(float('%.5g'% qstar_high)))
328        self.model.setItem(WIDGETS.D_HIGH_QSTAR, item)
329        item = QtGui.QStandardItem(str(float('%.5g'% qstar_high_err)))
330        self.model.setItem(WIDGETS.D_HIGH_QSTAR_ERR, item)
331
332        self.mapper.toFirst()
333
334        return self.model
335               
336    def title(self):
337        """
338        Perspective name
339        """
340        return "Invariant panel"
341
342    def status(self):
343        """
344        """
345        self.detailsDialog.setModel(self.model)
346        self.detailsDialog.showDialog()
347
348    def help(self):
349        """
350        """
351        _TreeLocation = "html/user/sasgui/perspectives/invariant/invariant_help.html"
352        self._helpView.load(QtCore.QUrl(_TreeLocation))
353        self._helpView.show()
354
355    def setupSlots(self):
356        self.pushButton.clicked.connect(self.calculateInvariant)
357        self.pushButton_2.clicked.connect(self.status)
358        self.pushButton_3.clicked.connect(self.help)
359
360        self.radioButton.toggled.connect(self.lowGuinierAndPowerToggle)
361        self.radioButton_8.toggled.connect(self.hiFitAndFixToggle)
362
363        self.radioButton_3.toggled.connect(self.lowFitAndFixToggle)
364
365        # Bug workaround for QDataWidgetMapper() not reacting to checkbox clicks.
366        # https://bugreports.qt.io/browse/QTBUG-1818
367        self.checkBox.clicked.connect(self.setFocus)
368        self.checkBox_2.clicked.connect(self.setFocus)
369
370        self.model.itemChanged.connect(self.modelChanged)
371
372    def modelChanged(self, item):
373        """
374        """
375        if item.row() == WIDGETS.W_ENABLE_LOWQ:
376            toggle = (str(item.text()) == 'true')
377            self._low_extrapolate = toggle
378            self.lowQToggle(toggle)
379        elif item.row() == WIDGETS.W_ENABLE_HIGHQ:
380            toggle = (str(item.text()) == 'true')
381            self._high_extrapolate = toggle
382            self.highQToggle(toggle)
383       
384    def lowGuinierAndPowerToggle(self, toggle):
385        """
386        """
387        self._low_guinier = toggle
388        toggle = not toggle
389        self.lineEdit_11.setEnabled(toggle)
390        self.radioButton_3.setEnabled(toggle)
391        self.radioButton_4.setEnabled(toggle)
392        self.lineEdit_11.setEnabled(toggle and (not self._low_fit))
393
394    def lowFitAndFixToggle(self, toggle):
395        """
396        """
397        self._low_fit = toggle
398        toggle = not toggle
399        self.lineEdit_11.setEnabled(toggle)
400
401    def hiFitAndFixToggle(self, toggle):
402        """
403        """
404        self.lineEdit_13.setEnabled(toggle)
405
406    def highQToggle(self, clicked):
407        """
408        Disable/enable High Q extrapolation
409        """
410        self.radioButton_7.setEnabled(clicked)
411        self.radioButton_8.setEnabled(clicked)
412        self.lineEdit_12.setEnabled(clicked)
413        self.lineEdit_13.setEnabled(clicked)
414
415    def lowQToggle(self, clicked):
416        """
417        Disable/enable Low Q extrapolation
418        """
419        self.radioButton.setEnabled(clicked)
420        self.radioButton_2.setEnabled(clicked)
421        self.lineEdit_11.setEnabled(clicked and not self._low_fit)
422
423        # Enable subelements
424        self.radioButton_3.setEnabled(clicked and not self._low_guinier)
425        self.radioButton_4.setEnabled(clicked and not self._low_guinier)
426        self.lineEdit_10.setEnabled(clicked and not self._low_guinier)
427
428    def setupModel(self):
429
430        # filename
431        item = QtGui.QStandardItem(self._path)
432        self.model.setItem(WIDGETS.W_FILENAME, item)
433
434        # add Q parameters to the model
435        qmin = min(self._data.x)
436        item = QtGui.QStandardItem(str(qmin))
437        self.model.setItem(WIDGETS.W_QMIN, item)
438        item = QtGui.QStandardItem(str(max(self._data.x)))
439        self.model.setItem(WIDGETS.W_QMAX, item)
440
441        # add custom input params
442        item = QtGui.QStandardItem(str(self._background))
443        self.model.setItem(WIDGETS.W_BACKGROUND, item)
444        item = QtGui.QStandardItem(str(self._contrast))
445        self.model.setItem(WIDGETS.W_CONTRAST, item)
446        item = QtGui.QStandardItem(str(self._scale))
447        self.model.setItem(WIDGETS.W_SCALE, item)
448       
449        # Dialog elements
450        itemf = QtGui.QStandardItem("false")
451        self.model.setItem(WIDGETS.W_ENABLE_HIGHQ, itemf)
452        itemf = QtGui.QStandardItem("false")
453        self.model.setItem(WIDGETS.W_ENABLE_LOWQ, itemf)
454
455        item = QtGui.QStandardItem(str(self._npoints_low))
456        self.model.setItem(WIDGETS.W_NPTS_LOWQ, item)
457        item = QtGui.QStandardItem(str(self._npoints_high))
458        self.model.setItem(WIDGETS.W_NPTS_HIGHQ, item)
459
460        itemt = QtGui.QStandardItem("true")
461        self.model.setItem(WIDGETS.W_LOWQ_GUINIER, itemt)
462
463        itemt = QtGui.QStandardItem("true")
464        self.model.setItem(WIDGETS.W_LOWQ_FIT, itemt)
465        item = QtGui.QStandardItem(str(self._power_low))
466        self.model.setItem(WIDGETS.W_LOWQ_POWER_VALUE, item)
467
468        itemt = QtGui.QStandardItem("true")
469        self.model.setItem(WIDGETS.W_HIGHQ_FIT, itemt)
470        item = QtGui.QStandardItem(str(self._power_low))
471        self.model.setItem(WIDGETS.W_HIGHQ_POWER_VALUE, item)
472
473
474    def setupMapper(self):
475        # Set up the mapper.
476        self.mapper = QtGui.QDataWidgetMapper(self)
477        self.mapper.setOrientation(QtCore.Qt.Vertical)
478        self.mapper.setModel(self.model)
479
480        # Set up the view on the model for testing
481        # self.tableView.setModel(self.model)
482
483        # Filename
484        self.mapper.addMapping(self.lineEdit, WIDGETS.W_FILENAME)
485        # Qmin/Qmax
486        self.mapper.addMapping(self.lineEdit_2, WIDGETS.W_QMIN)
487        self.mapper.addMapping(self.lineEdit_3, WIDGETS.W_QMAX)
488
489        # Background
490        self.mapper.addMapping(self.lineEdit_4, WIDGETS.W_BACKGROUND)
491        # Scale
492        self.mapper.addMapping(self.lineEdit_5, WIDGETS.W_SCALE)
493        # Contrast
494        self.mapper.addMapping(self.lineEdit_6, WIDGETS.W_CONTRAST)
495
496        # Lowq/Highq items
497        self.mapper.addMapping(self.checkBox, WIDGETS.W_ENABLE_LOWQ)
498        self.mapper.addMapping(self.checkBox_2, WIDGETS.W_ENABLE_HIGHQ)
499
500        self.mapper.addMapping(self.lineEdit_10, WIDGETS.W_NPTS_LOWQ)
501        self.mapper.addMapping(self.radioButton, WIDGETS.W_LOWQ_GUINIER)
502
503        self.mapper.addMapping(self.radioButton_3, WIDGETS.W_LOWQ_FIT)
504        self.mapper.addMapping(self.lineEdit_11, WIDGETS.W_LOWQ_POWER_VALUE)
505
506        self.mapper.addMapping(self.radioButton_7, WIDGETS.W_HIGHQ_FIT)
507        self.mapper.addMapping(self.lineEdit_13, WIDGETS.W_HIGHQ_POWER_VALUE)
508   
509        # Output
510        self.mapper.addMapping(self.lineEdit_14, WIDGETS.W_VOLUME_FRACTION)
511        self.mapper.addMapping(self.lineEdit_15, WIDGETS.W_VOLUME_FRACTION_ERR)
512        self.mapper.addMapping(self.lineEdit_16, WIDGETS.W_SPECIFIC_SURFACE)
513        self.mapper.addMapping(self.lineEdit_17, WIDGETS.W_SPECIFIC_SURFACE_ERR)
514        self.mapper.addMapping(self.lineEdit_19, WIDGETS.W_INVARIANT)
515        self.mapper.addMapping(self.lineEdit_18, WIDGETS.W_INVARIANT_ERR)
516
517        self.mapper.toFirst()
518
519    def setData(self, data_item):
520        """
521        Obtain a QStandardItem object and dissect it to get Data1D/2D
522        Pass it over to the calculator
523        """
524        if not isinstance(data_item, list):
525            msg = "Incorrect type passed to the Invariant Perspective"
526            raise AttributeError, msg
527
528        if not isinstance(data_item[0], QtGui.QStandardItem):
529            msg = "Incorrect type passed to the Invariant Perspective"
530            raise AttributeError, msg
531
532        self._model_item = data_item[0]
533
534        # Extract data on 1st child - this is the Data1D/2D component
535        data = self._model_item.child(0).data().toPyObject()
536
537        self.calculate(data_list=[data])
538       
539    def calculate(self, data_list=None):
540        """
541        receive a list of data and compute invariant
542
543        TODO: pass warnings/messages to log
544        """
545        msg = ""
546        data = None
547        if data_list is None:
548            data_list = []
549        if len(data_list) >= 1:
550            if len(data_list) == 1:
551                data = data_list[0]
552            else:
553                data_1d_list = []
554                data_2d_list = []
555                error_msg = ""
556                # separate data into data1d and data2d list
557                for data in data_list:
558                    if data is not None:
559                        if issubclass(data.__class__, Data1D):
560                            data_1d_list.append(data)
561                        else:
562                            error_msg += " %s  type %s \n" % (str(data.name),
563                                                              str(data.__class__.__name__))
564                            data_2d_list.append(data)
565                if len(data_2d_list) > 0:
566                    msg = "Invariant does not support the following data types:\n"
567                    msg += error_msg
568                if len(data_1d_list) == 0:
569                    # remake this as a qt event
570                    #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
571                    return
572
573                # TODO: add msgbox for data choice
574                #msg += "Invariant panel does not allow multiple data!\n"
575                #msg += "Please select one.\n"
576                #if len(data_list) > 1:
577                    #from invariant_widgets import DataDialog
578                    #dlg = DataDialog(data_list=data_1d_list, text=msg)
579                    #if dlg.ShowModal() == wx.ID_OK:
580                    #    data = dlg.get_data()
581                    #else:
582                    #    data = None
583                    #dlg.Destroy()
584
585            if data is None:
586                msg += "invariant receives no data. \n"
587                #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
588                return
589            if not issubclass(data.__class__, Data1D):
590                msg += "invariant cannot be computed for data of "
591                msg += "type %s\n" % (data.__class__.__name__)
592                #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
593                return
594            else:
595                #wx.PostEvent(self.parent, NewPlotEvent(plot=data, title=data.title))
596                try:
597                    self._data = data
598                    self._path = "unique path"
599                    self.calculateInvariant()
600                except:
601                    msg = "Invariant Set_data: " + str(sys.exc_value)
602                    #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
603        else:
604            msg = "invariant cannot be computed for data of "
605            msg += "type %s" % (data.__class__.__name__)
606            #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
607
608    def allowBatch(self):
609        """
610        Tell the caller that we don't accept multiple data instances
611        """
612        return False
613
614if __name__ == "__main__":
615    app = QtGui.QApplication([])
616    import qt4reactor
617    qt4reactor.install()
618    # DO NOT move the following import to the top!
619    # (unless you know what you're doing)
620    from twisted.internet import reactor
621    dlg = InvariantWindow(reactor)
622    dlg.show()
623    reactor.run()
Note: See TracBrowser for help on using the repository browser.