source: sasview/src/sas/qtgui/Utilities/GuiUtils.py @ 53c771e

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

Converted unit tests

  • Property mode set to 100644
File size: 27.8 KB
Line 
1"""
2Global defaults and various utility functions usable by the general GUI
3"""
4
5import os
6import re
7import sys
8import imp
9import warnings
10import webbrowser
11import urllib.parse
12
13warnings.simplefilter("ignore")
14import logging
15
16from PyQt5 import QtCore
17from PyQt5 import QtGui
18from PyQt5 import QtWidgets
19
20from periodictable import formula as Formula
21from sas.qtgui.Plotting import DataTransform
22from sas.qtgui.Plotting.ConvertUnits import convertUnit
23from sas.qtgui.Plotting.PlotterData import Data1D
24from sas.qtgui.Plotting.PlotterData import Data2D
25from sas.sascalc.dataloader.loader import Loader
26from sas.qtgui.Utilities import CustomDir
27
28## TODO: CHANGE FOR SHIPPED PATH IN RELEASE
29if os.path.splitext(sys.argv[0])[1].lower() != ".py":
30        HELP_DIRECTORY_LOCATION = "doc"
31else:
32        HELP_DIRECTORY_LOCATION = "docs/sphinx-docs/build/html"
33IMAGES_DIRECTORY_LOCATION = HELP_DIRECTORY_LOCATION + "/_images"
34
35def get_app_dir():
36    """
37        The application directory is the one where the default custom_config.py
38        file resides.
39
40        :returns: app_path - the path to the applicatin directory
41    """
42    # First, try the directory of the executable we are running
43    app_path = sys.path[0]
44    if os.path.isfile(app_path):
45        app_path = os.path.dirname(app_path)
46    if os.path.isfile(os.path.join(app_path, "custom_config.py")):
47        app_path = os.path.abspath(app_path)
48        #logging.info("Using application path: %s", app_path)
49        return app_path
50
51    # Next, try the current working directory
52    if os.path.isfile(os.path.join(os.getcwd(), "custom_config.py")):
53        #logging.info("Using application path: %s", os.getcwd())
54        return os.path.abspath(os.getcwd())
55
56    # Finally, try the directory of the sasview module
57    # TODO: gui_manager will have to know about sasview until we
58    # clean all these module variables and put them into a config class
59    # that can be passed by sasview.py.
60    #logging.info(sys.executable)
61    #logging.info(str(sys.argv))
62    from sas import sasview as sasview
63    app_path = os.path.dirname(sasview.__file__)
64    #logging.info("Using application path: %s", app_path)
65    return app_path
66
67def get_user_directory():
68    """
69        Returns the user's home directory
70    """
71    userdir = os.path.join(os.path.expanduser("~"), ".sasview")
72    if not os.path.isdir(userdir):
73        os.makedirs(userdir)
74    return userdir
75
76def _find_local_config(confg_file, path):
77    """
78        Find configuration file for the current application
79    """
80    config_module = None
81    fObj = None
82    try:
83        fObj, path_config, descr = imp.find_module(confg_file, [path])
84        config_module = imp.load_module(confg_file, fObj, path_config, descr)
85    except ImportError:
86        pass
87        #logging.error("Error loading %s/%s: %s" % (path, confg_file, sys.exc_value))
88    except ValueError:
89        print("Value error")
90        pass
91    finally:
92        if fObj is not None:
93            fObj.close()
94    #logging.info("GuiManager loaded %s/%s" % (path, confg_file))
95    return config_module
96
97
98# Get APP folder
99PATH_APP = get_app_dir()
100DATAPATH = PATH_APP
101
102# Read in the local config, which can either be with the main
103# application or in the installation directory
104config = _find_local_config('local_config', PATH_APP)
105
106if config is None:
107    config = _find_local_config('local_config', os.getcwd())
108else:
109    pass
110
111c_conf_dir = CustomDir.setup_conf_dir(PATH_APP)
112
113custom_config = _find_local_config('custom_config', c_conf_dir)
114if custom_config is None:
115    custom_config = _find_local_config('custom_config', os.getcwd())
116    if custom_config is None:
117        msgConfig = "Custom_config file was not imported"
118
119#read some constants from config
120APPLICATION_STATE_EXTENSION = config.APPLICATION_STATE_EXTENSION
121APPLICATION_NAME = config.__appname__
122SPLASH_SCREEN_PATH = config.SPLASH_SCREEN_PATH
123WELCOME_PANEL_ON = config.WELCOME_PANEL_ON
124SPLASH_SCREEN_WIDTH = config.SPLASH_SCREEN_WIDTH
125SPLASH_SCREEN_HEIGHT = config.SPLASH_SCREEN_HEIGHT
126SS_MAX_DISPLAY_TIME = config.SS_MAX_DISPLAY_TIME
127if not WELCOME_PANEL_ON:
128    WELCOME_PANEL_SHOW = False
129else:
130    WELCOME_PANEL_SHOW = True
131try:
132    DATALOADER_SHOW = custom_config.DATALOADER_SHOW
133    TOOLBAR_SHOW = custom_config.TOOLBAR_SHOW
134    FIXED_PANEL = custom_config.FIXED_PANEL
135    if WELCOME_PANEL_ON:
136        WELCOME_PANEL_SHOW = custom_config.WELCOME_PANEL_SHOW
137    PLOPANEL_WIDTH = custom_config.PLOPANEL_WIDTH
138    DATAPANEL_WIDTH = custom_config.DATAPANEL_WIDTH
139    GUIFRAME_WIDTH = custom_config.GUIFRAME_WIDTH
140    GUIFRAME_HEIGHT = custom_config.GUIFRAME_HEIGHT
141    CONTROL_WIDTH = custom_config.CONTROL_WIDTH
142    CONTROL_HEIGHT = custom_config.CONTROL_HEIGHT
143    DEFAULT_PERSPECTIVE = custom_config.DEFAULT_PERSPECTIVE
144    CLEANUP_PLOT = custom_config.CLEANUP_PLOT
145    # custom open_path
146    open_folder = custom_config.DEFAULT_OPEN_FOLDER
147    if open_folder is not None and os.path.isdir(open_folder):
148        DEFAULT_OPEN_FOLDER = os.path.abspath(open_folder)
149    else:
150        DEFAULT_OPEN_FOLDER = PATH_APP
151except AttributeError:
152    DATALOADER_SHOW = True
153    TOOLBAR_SHOW = True
154    FIXED_PANEL = True
155    WELCOME_PANEL_SHOW = False
156    PLOPANEL_WIDTH = config.PLOPANEL_WIDTH
157    DATAPANEL_WIDTH = config.DATAPANEL_WIDTH
158    GUIFRAME_WIDTH = config.GUIFRAME_WIDTH
159    GUIFRAME_HEIGHT = config.GUIFRAME_HEIGHT
160    CONTROL_WIDTH = -1
161    CONTROL_HEIGHT = -1
162    DEFAULT_PERSPECTIVE = None
163    CLEANUP_PLOT = False
164    DEFAULT_OPEN_FOLDER = PATH_APP
165
166#DEFAULT_STYLE = config.DEFAULT_STYLE
167
168PLUGIN_STATE_EXTENSIONS = config.PLUGIN_STATE_EXTENSIONS
169OPEN_SAVE_MENU = config.OPEN_SAVE_PROJECT_MENU
170VIEW_MENU = config.VIEW_MENU
171EDIT_MENU = config.EDIT_MENU
172extension_list = []
173if APPLICATION_STATE_EXTENSION is not None:
174    extension_list.append(APPLICATION_STATE_EXTENSION)
175EXTENSIONS = PLUGIN_STATE_EXTENSIONS + extension_list
176try:
177    PLUGINS_WLIST = '|'.join(config.PLUGINS_WLIST)
178except AttributeError:
179    PLUGINS_WLIST = ''
180APPLICATION_WLIST = config.APPLICATION_WLIST
181IS_WIN = True
182IS_LINUX = False
183CLOSE_SHOW = True
184TIME_FACTOR = 2
185NOT_SO_GRAPH_LIST = ["BoxSum"]
186
187
188class Communicate(QtCore.QObject):
189    """
190    Utility class for tracking of the Qt signals
191    """
192    # File got successfully read
193    fileReadSignal = QtCore.pyqtSignal(list)
194
195    # Open File returns "list" of paths
196    fileDataReceivedSignal = QtCore.pyqtSignal(dict)
197
198    # Update Main window status bar with "str"
199    # Old "StatusEvent"
200    statusBarUpdateSignal = QtCore.pyqtSignal(str)
201
202    # Send data to the current perspective
203    updatePerspectiveWithDataSignal = QtCore.pyqtSignal(list)
204
205    # New data in current perspective
206    updateModelFromPerspectiveSignal = QtCore.pyqtSignal(QtGui.QStandardItem)
207
208    # New theory data in current perspective
209    updateTheoryFromPerspectiveSignal = QtCore.pyqtSignal(QtGui.QStandardItem)
210
211    # New plot requested from the GUI manager
212    # Old "NewPlotEvent"
213    plotRequestedSignal = QtCore.pyqtSignal(list)
214
215    # Plot update requested from a perspective
216    plotUpdateSignal = QtCore.pyqtSignal(list)
217
218    # Progress bar update value
219    progressBarUpdateSignal = QtCore.pyqtSignal(int)
220
221    # Workspace charts added/removed
222    activeGraphsSignal = QtCore.pyqtSignal(list)
223
224    # Current workspace chart's name changed
225    activeGraphName = QtCore.pyqtSignal(tuple)
226
227    # Current perspective changed
228    perspectiveChangedSignal = QtCore.pyqtSignal(str)
229
230    # File/dataset got deleted
231    dataDeletedSignal = QtCore.pyqtSignal(list)
232
233    # Send data to Data Operation Utility panel
234    sendDataToPanelSignal = QtCore.pyqtSignal(dict)
235
236    # Send result of Data Operation Utility panel to Data Explorer
237    updateModelFromDataOperationPanelSignal = QtCore.pyqtSignal(QtGui.QStandardItem, dict)
238
239def updateModelItemWithPlot(item, update_data, name=""):
240    """
241    Adds a checkboxed row named "name" to QStandardItem
242    Adds 'update_data' to that row.
243    """
244    assert isinstance(item, QtGui.QStandardItem)
245    py_update_data = update_data
246
247    # Check if data with the same ID is already present
248    for index in range(item.rowCount()):
249        plot_item = item.child(index)
250        if plot_item.isCheckable():
251            plot_data = plot_item.child(0).data()
252            if plot_data.id is not None and plot_data.id == py_update_data.id:
253                # replace data section in item
254                plot_item.child(0).setData(update_data)
255                plot_item.setText(name)
256                # Plot title
257                plot_item.child(1).child(0).setText("Title: %s"%name)
258                # Force redisplay
259                return
260
261    # Create the new item
262    checkbox_item = createModelItemWithPlot(update_data, name)
263
264    # Append the new row to the main item
265    item.appendRow(checkbox_item)
266
267def createModelItemWithPlot(update_data, name=""):
268    """
269    Creates a checkboxed QStandardItem named "name"
270    Adds 'update_data' to that row.
271    """
272    py_update_data = update_data
273
274    checkbox_item = QtGui.QStandardItem()
275    checkbox_item.setCheckable(True)
276    checkbox_item.setCheckState(QtCore.Qt.Checked)
277    checkbox_item.setText(name)
278
279    # Add "Info" item
280    if isinstance(py_update_data, (Data1D, Data2D)):
281        # If Data1/2D added - extract Info from it
282        info_item = infoFromData(py_update_data)
283    else:
284        # otherwise just add a naked item
285        info_item = QtGui.QStandardItem("Info")
286
287    # Add the actual Data1D/Data2D object
288    object_item = QtGui.QStandardItem()
289    object_item.setData(update_data)
290
291    # Set the data object as the first child
292    checkbox_item.setChild(0, object_item)
293
294    # Set info_item as the second child
295    checkbox_item.setChild(1, info_item)
296
297    # And return the newly created item
298    return checkbox_item
299
300def updateModelItem(item, update_data, name=""):
301    """
302    Adds a simple named child to QStandardItem
303    """
304    assert isinstance(item, QtGui.QStandardItem)
305    assert isinstance(update_data, list)
306
307    # Add the actual Data1D/Data2D object
308    object_item = QtGui.QStandardItem()
309    object_item.setText(name)
310    object_item.setData(update_data)
311
312    # Append the new row to the main item
313    item.appendRow(object_item)
314
315def itemFromFilename(filename, model_item):
316    """
317    Returns the model item text=filename in the model
318    """
319    assert isinstance(model_item, QtGui.QStandardItemModel)
320    assert isinstance(filename, str)
321
322    # Iterate over model looking for named items
323    item = list([i for i in [model_item.item(index) for index in range(model_item.rowCount())] if str(i.text()) == filename])
324    return item[0] if len(item)>0 else None
325
326def plotsFromFilename(filename, model_item):
327    """
328    Returns the list of plots for the item with text=filename in the model
329    """
330    assert isinstance(model_item, QtGui.QStandardItemModel)
331    assert isinstance(filename, str)
332
333    plot_data = []
334    # Iterate over model looking for named items
335    for index in range(model_item.rowCount()):
336        item = model_item.item(index)
337        if str(item.text()) == filename:
338            # TODO: assure item type is correct (either data1/2D or Plotter)
339            plot_data.append(item.child(0).data())
340            # Going 1 level deeper only
341            for index_2 in range(item.rowCount()):
342                item_2 = item.child(index_2)
343                if item_2 and item_2.isCheckable():
344                    # TODO: assure item type is correct (either data1/2D or Plotter)
345                    plot_data.append(item_2.child(0).data())
346
347    return plot_data
348
349def plotsFromCheckedItems(model_item):
350    """
351    Returns the list of plots for items in the model which are checked
352    """
353    assert isinstance(model_item, QtGui.QStandardItemModel)
354
355    plot_data = []
356    # Iterate over model looking for items with checkboxes
357    for index in range(model_item.rowCount()):
358        item = model_item.item(index)
359        if item.isCheckable() and item.checkState() == QtCore.Qt.Checked:
360            # TODO: assure item type is correct (either data1/2D or Plotter)
361            plot_data.append((item, item.child(0).data()))
362        # Going 1 level deeper only
363        for index_2 in range(item.rowCount()):
364            item_2 = item.child(index_2)
365            if item_2 and item_2.isCheckable() and item_2.checkState() == QtCore.Qt.Checked:
366                # TODO: assure item type is correct (either data1/2D or Plotter)
367                plot_data.append((item_2, item_2.child(0).data()))
368
369    return plot_data
370
371def infoFromData(data):
372    """
373    Given Data1D/Data2D object, extract relevant Info elements
374    and add them to a model item
375    """
376    assert isinstance(data, (Data1D, Data2D))
377
378    info_item = QtGui.QStandardItem("Info")
379
380    title_item = QtGui.QStandardItem("Title: " + data.title)
381    info_item.appendRow(title_item)
382    run_item = QtGui.QStandardItem("Run: " + str(data.run))
383    info_item.appendRow(run_item)
384    type_item = QtGui.QStandardItem("Type: " + str(data.__class__.__name__))
385    info_item.appendRow(type_item)
386
387    if data.path:
388        path_item = QtGui.QStandardItem("Path: " + data.path)
389        info_item.appendRow(path_item)
390
391    if data.instrument:
392        instr_item = QtGui.QStandardItem("Instrument: " + data.instrument)
393        info_item.appendRow(instr_item)
394
395    process_item = QtGui.QStandardItem("Process")
396    if isinstance(data.process, list) and data.process:
397        for process in data.process:
398            process_date = process.date
399            process_date_item = QtGui.QStandardItem("Date: " + process_date)
400            process_item.appendRow(process_date_item)
401
402            process_descr = process.description
403            process_descr_item = QtGui.QStandardItem("Description: " + process_descr)
404            process_item.appendRow(process_descr_item)
405
406            process_name = process.name
407            process_name_item = QtGui.QStandardItem("Name: " + process_name)
408            process_item.appendRow(process_name_item)
409
410    info_item.appendRow(process_item)
411
412    return info_item
413
414def openLink(url):
415    """
416    Open a URL in an external browser.
417    Check the URL first, though.
418    """
419    parsed_url = urllib.parse.urlparse(url)
420    if parsed_url.scheme:
421        webbrowser.open(url)
422    else:
423        msg = "Attempt at opening an invalid URL"
424        raise AttributeError(msg)
425
426def retrieveData1d(data):
427    """
428    Retrieve 1D data from file and construct its text
429    representation
430    """
431    if not isinstance(data, Data1D):
432        msg = "Incorrect type passed to retrieveData1d"
433        raise AttributeError(msg)
434    try:
435        xmin = min(data.x)
436        ymin = min(data.y)
437    except:
438        msg = "Unable to find min/max of \n data named %s" % \
439                    data.filename
440        #logging.error(msg)
441        raise ValueError(msg)
442
443    text = data.__str__()
444    text += 'Data Min Max:\n'
445    text += 'X_min = %s:  X_max = %s\n' % (xmin, max(data.x))
446    text += 'Y_min = %s:  Y_max = %s\n' % (ymin, max(data.y))
447    if data.dy is not None:
448        text += 'dY_min = %s:  dY_max = %s\n' % (min(data.dy), max(data.dy))
449    text += '\nData Points:\n'
450    x_st = "X"
451    for index in range(len(data.x)):
452        if data.dy is not None and len(data.dy) > index:
453            dy_val = data.dy[index]
454        else:
455            dy_val = 0.0
456        if data.dx is not None and len(data.dx) > index:
457            dx_val = data.dx[index]
458        else:
459            dx_val = 0.0
460        if data.dxl is not None and len(data.dxl) > index:
461            if index == 0:
462                x_st = "Xl"
463            dx_val = data.dxl[index]
464        elif data.dxw is not None and len(data.dxw) > index:
465            if index == 0:
466                x_st = "Xw"
467            dx_val = data.dxw[index]
468
469        if index == 0:
470            text += "<index> \t<X> \t<Y> \t<dY> \t<d%s>\n" % x_st
471        text += "%s \t%s \t%s \t%s \t%s\n" % (index,
472                                                data.x[index],
473                                                data.y[index],
474                                                dy_val,
475                                                dx_val)
476    return text
477
478def retrieveData2d(data):
479    """
480    Retrieve 2D data from file and construct its text
481    representation
482    """
483    if not isinstance(data, Data2D):
484        msg = "Incorrect type passed to retrieveData2d"
485        raise AttributeError(msg)
486
487    text = data.__str__()
488    text += 'Data Min Max:\n'
489    text += 'I_min = %s\n' % min(data.data)
490    text += 'I_max = %s\n\n' % max(data.data)
491    text += 'Data (First 2501) Points:\n'
492    text += 'Data columns include err(I).\n'
493    text += 'ASCII data starts here.\n'
494    text += "<index> \t<Qx> \t<Qy> \t<I> \t<dI> \t<dQparal> \t<dQperp>\n"
495    di_val = 0.0
496    dx_val = 0.0
497    dy_val = 0.0
498    len_data = len(data.qx_data)
499    for index in range(0, len_data):
500        x_val = data.qx_data[index]
501        y_val = data.qy_data[index]
502        i_val = data.data[index]
503        if data.err_data is not None:
504            di_val = data.err_data[index]
505        if data.dqx_data is not None:
506            dx_val = data.dqx_data[index]
507        if data.dqy_data is not None:
508            dy_val = data.dqy_data[index]
509
510        text += "%s \t%s \t%s \t%s \t%s \t%s \t%s\n" % (index,
511                                                        x_val,
512                                                        y_val,
513                                                        i_val,
514                                                        di_val,
515                                                        dx_val,
516                                                        dy_val)
517        # Takes too long time for typical data2d: Break here
518        if index >= 2500:
519            text += ".............\n"
520            break
521
522    return text
523
524def onTXTSave(data, path):
525    """
526    Save file as formatted txt
527    """
528    with open(path,'w') as out:
529        has_errors = True
530        if data.dy is None or not data.dy:
531            has_errors = False
532        # Sanity check
533        if has_errors:
534            try:
535                if len(data.y) != len(data.dy):
536                    has_errors = False
537            except:
538                has_errors = False
539        if has_errors:
540            if data.dx is not None and data.dx:
541                out.write("<X>   <Y>   <dY>   <dX>\n")
542            else:
543                out.write("<X>   <Y>   <dY>\n")
544        else:
545            out.write("<X>   <Y>\n")
546
547        for i in range(len(data.x)):
548            if has_errors:
549                if data.dx is not None and data.dx:
550                    if  data.dx[i] is not None:
551                        out.write("%g  %g  %g  %g\n" % (data.x[i],
552                                                        data.y[i],
553                                                        data.dy[i],
554                                                        data.dx[i]))
555                    else:
556                        out.write("%g  %g  %g\n" % (data.x[i],
557                                                    data.y[i],
558                                                    data.dy[i]))
559                else:
560                    out.write("%g  %g  %g\n" % (data.x[i],
561                                                data.y[i],
562                                                data.dy[i]))
563            else:
564                out.write("%g  %g\n" % (data.x[i],
565                                        data.y[i]))
566
567def saveData1D(data):
568    """
569    Save 1D data points
570    """
571    default_name = os.path.basename(data.filename)
572    default_name, extension = os.path.splitext(default_name)
573    if not extension:
574        extension = ".txt"
575    default_name += "_out" + extension
576
577    wildcard = "Text files (*.txt);;"\
578                "CanSAS 1D files(*.xml)"
579    kwargs = {
580        'caption'   : 'Save As',
581        'directory' : default_name,
582        'filter'    : wildcard,
583        'parent'    : None,
584    }
585    # Query user for filename.
586    filename_tuple = QtWidgets.QFileDialog.getSaveFileName(**kwargs)
587    filename = filename_tuple[0]
588
589    # User cancelled.
590    if not filename:
591        return
592
593    #Instantiate a loader
594    loader = Loader()
595    if os.path.splitext(filename)[1].lower() == ".txt":
596        onTXTSave(data, filename)
597    if os.path.splitext(filename)[1].lower() == ".xml":
598        loader.save(filename, data, ".xml")
599
600def saveData2D(data):
601    """
602    Save data2d dialog
603    """
604    default_name = os.path.basename(data.filename)
605    default_name, _ = os.path.splitext(default_name)
606    ext_format = ".dat"
607    default_name += "_out" + ext_format
608
609    wildcard = "IGOR/DAT 2D file in Q_map (*.dat)"
610    kwargs = {
611        'caption'   : 'Save As',
612        'directory' : default_name,
613        'filter'    : wildcard,
614        'parent'    : None,
615    }
616    # Query user for filename.
617    filename_tuple = QtWidgets.QFileDialog.getSaveFileName(**kwargs)
618    filename = filename_tuple[0]
619
620    # User cancelled.
621    if not filename:
622        return
623
624    #Instantiate a loader
625    loader = Loader()
626
627    if os.path.splitext(filename)[1].lower() == ext_format:
628        loader.save(filename, data, ext_format)
629
630class FormulaValidator(QtGui.QValidator):
631    def __init__(self, parent=None):
632        super(FormulaValidator, self).__init__(parent)
633 
634    def validate(self, input, pos):
635
636        self._setStyleSheet("")
637        return QtGui.QValidator.Acceptable, pos
638
639        #try:
640        #    Formula(str(input))
641        #    self._setStyleSheet("")
642        #    return QtGui.QValidator.Acceptable, pos
643
644        #except Exception as e:
645        #    self._setStyleSheet("background-color:pink;")
646        #    return QtGui.QValidator.Intermediate, pos
647
648    def _setStyleSheet(self, value):
649        try:
650            if self.parent():
651                self.parent().setStyleSheet(value)
652        except:
653            pass
654
655def xyTransform(data, xLabel="", yLabel=""):
656    """
657    Transforms x and y in View and set the scale
658    """
659    # Changing the scale might be incompatible with
660    # currently displayed data (for instance, going
661    # from ln to log when all plotted values have
662    # negative natural logs).
663    # Go linear and only change the scale at the end.
664    xscale = 'linear'
665    yscale = 'linear'
666    # Local data is either 1D or 2D
667    if data.id == 'fit':
668        return
669
670    # control axis labels from the panel itself
671    yname, yunits = data.get_yaxis()
672    xname, xunits = data.get_xaxis()
673
674    # Goes through all possible scales
675    # self.x_label is already wrapped with Latex "$", so using the argument
676
677    # X
678    if xLabel == "x":
679        data.transformX(DataTransform.toX, DataTransform.errToX)
680        xLabel = "%s(%s)" % (xname, xunits)
681    if xLabel == "x^(2)":
682        data.transformX(DataTransform.toX2, DataTransform.errToX2)
683        xunits = convertUnit(2, xunits)
684        xLabel = "%s^{2}(%s)" % (xname, xunits)
685    if xLabel == "x^(4)":
686        data.transformX(DataTransform.toX4, DataTransform.errToX4)
687        xunits = convertUnit(4, xunits)
688        xLabel = "%s^{4}(%s)" % (xname, xunits)
689    if xLabel == "ln(x)":
690        data.transformX(DataTransform.toLogX, DataTransform.errToLogX)
691        xLabel = "\ln{(%s)}(%s)" % (xname, xunits)
692    if xLabel == "log10(x)":
693        data.transformX(DataTransform.toX_pos, DataTransform.errToX_pos)
694        xscale = 'log'
695        xLabel = "%s(%s)" % (xname, xunits)
696    if xLabel == "log10(x^(4))":
697        data.transformX(DataTransform.toX4, DataTransform.errToX4)
698        xunits = convertUnit(4, xunits)
699        xLabel = "%s^{4}(%s)" % (xname, xunits)
700        xscale = 'log'
701
702    # Y
703    if yLabel == "ln(y)":
704        data.transformY(DataTransform.toLogX, DataTransform.errToLogX)
705        yLabel = "\ln{(%s)}(%s)" % (yname, yunits)
706    if yLabel == "y":
707        data.transformY(DataTransform.toX, DataTransform.errToX)
708        yLabel = "%s(%s)" % (yname, yunits)
709    if yLabel == "log10(y)":
710        data.transformY(DataTransform.toX_pos, DataTransform.errToX_pos)
711        yscale = 'log'
712        yLabel = "%s(%s)" % (yname, yunits)
713    if yLabel == "y^(2)":
714        data.transformY(DataTransform.toX2, DataTransform.errToX2)
715        yunits = convertUnit(2, yunits)
716        yLabel = "%s^{2}(%s)" % (yname, yunits)
717    if yLabel == "1/y":
718        data.transformY(DataTransform.toOneOverX, DataTransform.errOneOverX)
719        yunits = convertUnit(-1, yunits)
720        yLabel = "1/%s(%s)" % (yname, yunits)
721    if yLabel == "y*x^(2)":
722        data.transformY(DataTransform.toYX2, DataTransform.errToYX2)
723        xunits = convertUnit(2, xunits)
724        yLabel = "%s \ \ %s^{2}(%s%s)" % (yname, xname, yunits, xunits)
725    if yLabel == "y*x^(4)":
726        data.transformY(DataTransform.toYX4, DataTransform.errToYX4)
727        xunits = convertUnit(4, xunits)
728        yLabel = "%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits)
729    if yLabel == "1/sqrt(y)":
730        data.transformY(DataTransform.toOneOverSqrtX, DataTransform.errOneOverSqrtX)
731        yunits = convertUnit(-0.5, yunits)
732        yLabel = "1/\sqrt{%s}(%s)" % (yname, yunits)
733    if yLabel == "ln(y*x)":
734        data.transformY(DataTransform.toLogXY, DataTransform.errToLogXY)
735        yLabel = "\ln{(%s \ \ %s)}(%s%s)" % (yname, xname, yunits, xunits)
736    if yLabel == "ln(y*x^(2))":
737        data.transformY(DataTransform.toLogYX2, DataTransform.errToLogYX2)
738        xunits = convertUnit(2, xunits)
739        yLabel = "\ln (%s \ \ %s^{2})(%s%s)" % (yname, xname, yunits, xunits)
740    if yLabel == "ln(y*x^(4))":
741        data.transformY(DataTransform.toLogYX4, DataTransform.errToLogYX4)
742        xunits = convertUnit(4, xunits)
743        yLabel = "\ln (%s \ \ %s^{4})(%s%s)" % (yname, xname, yunits, xunits)
744    if yLabel == "log10(y*x^(4))":
745        data.transformY(DataTransform.toYX4, DataTransform.errToYX4)
746        xunits = convertUnit(4, xunits)
747        yscale = 'log'
748        yLabel = "%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits)
749
750    # Perform the transformation of data in data1d->View
751    data.transformView()
752
753    return (xLabel, yLabel, xscale, yscale)
754
755def dataFromItem(item):
756    """
757    Retrieve Data1D/2D component from QStandardItem.
758    The assumption - data stored in SasView standard, in child 0
759    """
760    return item.child(0).data()
761
762def formatNumber(value, high=False):
763    """
764    Return a float in a standardized, human-readable formatted string.
765    This is used to output readable (e.g. x.xxxe-y) values to the panel.
766    """
767    try:
768        value = float(value)
769    except:
770        output = "NaN"
771        return output.lstrip().rstrip()
772
773    if high:
774        output = "%-7.5g" % value
775
776    else:
777        output = "%-5.3g" % value
778    return output.lstrip().rstrip()
779
780def convertUnitToHTML(unit):
781    """
782    Convert ASCII unit display into well rendering HTML
783    """
784    if unit == "1/A":
785        return "&#x212B;<sup>-1</sup>"
786    elif unit == "1/cm":
787        return "cm<sup>-1</sup>"
788    elif unit == "Ang":
789        return "&#x212B;"
790    elif unit == "1e-6/Ang^2":
791        return "10<sup>-6</sup>/&#x212B;<sup>2</sup>"
792    elif unit == "inf":
793        return "&#x221e;"
794    elif unit == "-inf":
795        return "-&#x221e;"
796    else:
797        return unit
798
799def parseName(name, expression):
800    """
801    remove "_" in front of a name
802    """
803    if re.match(expression, name) is not None:
804        word = re.split(expression, name, 1)
805        for item in word:           
806            if item.lstrip().rstrip() != '':
807                return item
808    else:
809        return name
810
811def toDouble(value_string):
812    """
813    toFloat conversion which cares deeply about user's locale
814    """
815    # Holy shit this escalated quickly in Qt5.
816    # No more float() cast on general locales.
817    value = QtCore.QLocale().toFloat(value_string)
818    if value[1]:
819        return value[0]
820
821    # Try generic locale
822    value = QtCore.QLocale(QtCore.QLocale('en_US')).toFloat(value_string)
823    if value[1]:
824        return value[0]
825    else:
826        raise ValueError
827
828class DoubleValidator(QtGui.QDoubleValidator):
829    """
830    Allow only dots as decimal separator
831    """
832    def validate(self, input, pos):
833        """
834        Return invalid for commas
835        """
836        if (',' in input):
837            return (QtGui.QValidator.Invalid, input, pos)
838        return super(DoubleValidator, self).validate(input, pos)
839
840    def fixup(self, input):
841        """
842        Correct (remove) potential preexisting content
843        """
844        QtGui.QDoubleValidator.fixup(input)
845        input = input.replace(",", "")
846
Note: See TracBrowser for help on using the repository browser.