source: trunk/GSASII.py @ 2047

Last change on this file since 2047 was 2047, checked in by toby, 8 years ago

revise autoint to implement Reset -- redoes integration; warn on old image read

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 176.8 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2015-11-12 22:55:14 +0000 (Thu, 12 Nov 2015) $
6# $Author: toby $
7# $Revision: 2047 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2047 2015-11-12 22:55:14Z toby $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. Restarting GSAS-II')
59    print('*******************************************************')         
60    loc = os.path.dirname(__file__)
61    import subprocess
62    subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
63    sys.exit()
64   
65# load the GSAS routines
66import GSASIIpath
67GSASIIpath.SetVersionNumber("$Revision: 2047 $")
68import GSASIIIO as G2IO
69import GSASIIgrid as G2gd
70import GSASIIctrls as G2G
71import GSASIIplot as G2plt
72import GSASIIpwd as G2pwd
73import GSASIIpwdGUI as G2pdG
74import GSASIIphsGUI as G2phsG
75import GSASIIspc as G2spc
76import GSASIIstrMain as G2stMn
77import GSASIIstrIO as G2stIO
78import GSASIImapvars as G2mv
79import GSASIIobj as G2obj
80import GSASIIlattice as G2lat
81import GSASIIlog as log
82
83__version__ = '0.2.0'
84
85# PATCH: for Mavericks (OS X 10.9.x), wx produces an annoying warning about LucidaGrandeUI.
86# In case stderr has been suppressed there, redirect python error output to stdout. Nobody
87# else should care much about this.
88sys.stderr = sys.stdout
89
90def create(parent):
91    return GSASII(parent)
92
93def SetDefaultDData(dType,histoName,NShkl=0,NDij=0):
94    if dType in ['SXC','SNC']:
95        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
96            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
97            'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
98            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}],
99            'Flack':[0.0,False]}
100    elif dType == 'SNT':
101        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
102            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
103            'Extinction':['Lorentzian','None', {
104            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}]}
105    elif 'P' in dType:
106        return {'Histogram':histoName,'Show':False,'Scale':[1.0,False],
107            'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{},[],0.1],
108            'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
109                [1.,1.,1.,0.,0.,0.],6*[False,]],
110            'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
111                NShkl*[0.01,],NShkl*[False,]],
112            'HStrain':[NDij*[0.0,],NDij*[False,]],                         
113            'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
114
115class GSASII(wx.Frame):
116    '''Define the main GSAS-II frame and its associated menu items
117    '''
118    def MenuBinding(self,event):
119        '''Called when a menu is clicked upon; looks up the binding in table
120        '''
121        log.InvokeMenuCommand(event.GetId(),self,event)
122           
123    def Bind(self,eventtype,handler,*args,**kwargs):
124        '''Override the Bind function so that we can wrap calls that will be logged.
125       
126        N.B. This is a bit kludgy. Menu bindings with an id are wrapped and
127        menu bindings with an object and no id are not.
128        '''
129        if eventtype == wx.EVT_MENU and 'id' in kwargs:
130            menulabels = log.SaveMenuCommand(kwargs['id'],self,handler)
131            if menulabels:
132                wx.Frame.Bind(self,eventtype,self.MenuBinding,*args,**kwargs)
133                return
134        wx.Frame.Bind(self,eventtype,handler,*args,**kwargs)     
135   
136    def _Add_FileMenuItems(self, parent):
137        item = parent.Append(
138            help='Open a GSAS-II project file (*.gpx)', id=wx.ID_ANY,
139            kind=wx.ITEM_NORMAL,text='&Open project...')
140        self.Bind(wx.EVT_MENU, self.OnFileOpen, id=item.GetId())
141        item = parent.Append(
142            help='Save project under current name', id=wx.ID_ANY,
143            kind=wx.ITEM_NORMAL,text='&Save project')
144        self.Bind(wx.EVT_MENU, self.OnFileSave, id=item.GetId())
145        item = parent.Append(
146            help='Save current project to new file', id=wx.ID_ANY,
147            kind=wx.ITEM_NORMAL,text='Save project as...')
148        self.Bind(wx.EVT_MENU, self.OnFileSaveas, id=item.GetId())
149        item = parent.Append(
150            help='Create empty new project, saving current is optional', id=wx.ID_ANY,
151            kind=wx.ITEM_NORMAL,text='&New project')
152        self.Bind(wx.EVT_MENU, self.OnFileClose, id=item.GetId())
153        item = parent.Append(
154            help='Exit from GSAS-II', id=wx.ID_ANY,
155            kind=wx.ITEM_NORMAL,text='&Exit')
156        self.Bind(wx.EVT_MENU, self.OnFileExit, id=item.GetId())
157       
158    def _Add_DataMenuItems(self,parent):
159        item = parent.Append(
160            help='',id=wx.ID_ANY,
161            kind=wx.ITEM_NORMAL,
162            text='Read image data...')
163        self.Bind(wx.EVT_MENU, self.OnImageRead, id=item.GetId())
164        item = parent.Append(
165            help='',id=wx.ID_ANY,
166            kind=wx.ITEM_NORMAL,
167            text='Read Powder Pattern Peaks...')
168        self.Bind(wx.EVT_MENU, self.OnReadPowderPeaks, id=item.GetId())
169        item = parent.Append(
170            help='',id=wx.ID_ANY,
171            kind=wx.ITEM_NORMAL,
172            text='Sum powder data')
173        self.Bind(wx.EVT_MENU, self.OnPwdrSum, id=item.GetId())
174        item = parent.Append(
175            help='',id=wx.ID_ANY,
176            kind=wx.ITEM_NORMAL,
177            text='Sum image data')
178        self.Bind(wx.EVT_MENU, self.OnImageSum, id=item.GetId())
179        item = parent.Append(
180            help='',id=wx.ID_ANY,
181            kind=wx.ITEM_NORMAL,
182            text='Add new phase')
183        self.Bind(wx.EVT_MENU, self.OnAddPhase, id=item.GetId())
184        item = parent.Append(
185            help='',id=wx.ID_ANY,
186            kind=wx.ITEM_NORMAL,
187            text='Delete phase')
188        self.Bind(wx.EVT_MENU, self.OnDeletePhase, id=item.GetId())
189        item = parent.Append(
190            help='',id=wx.ID_ANY,
191            kind=wx.ITEM_NORMAL,
192            text='Rename data') 
193        self.Bind(wx.EVT_MENU, self.OnRenameData, id=item.GetId())
194        item = parent.Append(
195            help='',id=wx.ID_ANY,
196            kind=wx.ITEM_NORMAL,
197            text='Delete data')
198        self.Bind(wx.EVT_MENU, self.OnDataDelete, id=item.GetId())
199               
200    def _Add_CalculateMenuItems(self,parent):
201        item = parent.Append(help='Make new PDFs from selected powder patterns', 
202            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='Make new PDFs')
203        self.MakePDF.append(item)
204#        item.Enable(False)
205        self.Bind(wx.EVT_MENU, self.OnMakePDFs, id=item.GetId())
206       
207        item = parent.Append(help='View least squares parameters', 
208            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='&View LS parms')
209        self.Bind(wx.EVT_MENU, self.ShowLSParms, id=item.GetId())
210       
211        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
212            text='&Refine')
213        if len(self.Refine): # extend state for new menus to match main (on mac)
214            state = self.Refine[0].IsEnabled()
215        else:
216            state = False
217        item.Enable(state)
218        self.Refine.append(item)
219        self.Bind(wx.EVT_MENU, self.OnRefine, id=item.GetId())
220       
221        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
222            text='Sequential refine')
223        if len(self.SeqRefine): # extend state for new menus to match main (on mac)
224            state = self.SeqRefine[0].IsEnabled()
225        else:
226            state = False
227        item.Enable(state)
228        self.SeqRefine.append(item) # save menu obj for use in self.EnableSeqRefineMenu
229        self.Bind(wx.EVT_MENU, self.OnSeqRefine, id=item.GetId())
230       
231    def _init_Imports(self):
232        '''import all the G2phase*.py & G2sfact*.py & G2pwd*.py files that
233        are found in the path
234        '''
235
236        self.ImportPhaseReaderlist = []
237        self._init_Import_routines('phase',self.ImportPhaseReaderlist,'Phase')
238        self.ImportSfactReaderlist = []
239        self._init_Import_routines('sfact',self.ImportSfactReaderlist,'Struct_Factor')
240        self.ImportPowderReaderlist = []
241        self._init_Import_routines('pwd',self.ImportPowderReaderlist,'Powder_Data')
242        self.ImportSmallAngleReaderlist = []
243        self._init_Import_routines('sad',self.ImportSmallAngleReaderlist,'SmallAngle_Data')
244        self.ImportImageReaderlist = []
245        self._init_Import_routines('img',self.ImportImageReaderlist,'Images')
246        self.ImportMenuId = {}
247
248    def _init_Import_routines(self,prefix,readerlist,errprefix):
249        '''import all the import readers matching the prefix
250        '''
251        #path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file
252        #pathlist = sys.path[:]
253        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
254        #path2GSAS2 = os.path.join(
255        #    os.path.dirname(os.path.realpath(__file__)), # location of this file
256        #    'imports')
257        pathlist = sys.path[:]
258        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
259        if '.' not in pathlist: pathlist.append('.') # insert the directory where G2 is started
260
261        filelist = []
262        for path in pathlist:
263            for filename in glob.iglob(os.path.join(
264                path,
265                "G2"+prefix+"*.py")):
266                filelist.append(filename)   
267                #print 'debug: found',filename
268        filelist = sorted(list(set(filelist))) # remove duplicates
269        for filename in filelist:
270            path,rootname = os.path.split(filename)
271            pkg = os.path.splitext(rootname)[0]
272            try:
273                fp = None
274                fp, fppath,desc = imp.find_module(pkg,[path,])
275                pkg = imp.load_module(pkg,fp,fppath,desc)
276                for clss in inspect.getmembers(pkg): # find classes defined in package
277                    if clss[0].startswith('_'): continue
278                    if inspect.isclass(clss[1]):
279                        # check if we have the required methods
280                        for m in 'Reader','ExtensionValidator','ContentsValidator':
281                            if not hasattr(clss[1],m): break
282                            if not callable(getattr(clss[1],m)): break
283                        else:
284                            reader = clss[1]() # create an import instance
285                            readerlist.append(reader)
286            except AttributeError:
287                print 'Import_'+errprefix+': Attribute Error '+str(filename)
288            #except ImportError:
289            #    print 'Import_'+errprefix+': Error importing file '+str(filename)
290            except Exception,errmsg:
291                print('\nImport_'+errprefix+': Error importing file '+str(filename))
292                print('Error message: '+str(errmsg)+'\n')
293            if fp: fp.close()
294
295    def EnableSeqRefineMenu(self):
296        '''Enable or disable the sequential refinement menu items based on the
297        contents of the Controls 'Seq Data' item (if present)
298        '''
299        controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
300        if controls.get('Seq Data'):
301            for i in self.SeqRefine: i.Enable(True)
302        else:
303            for i in self.SeqRefine: i.Enable(False)
304
305    def PreviewFile(self,filename,fp):
306        'confirm we have the right file'
307        rdmsg = 'File '+str(filename)+' begins:\n\n'
308        for i in range(3):
309            rdmsg += fp.readline()
310        rdmsg += '\n\nDo you want to read this file?'
311        if not all([ord(c) < 128 and ord(c) != 0 for c in rdmsg]): # show only if ASCII
312            rdmsg = 'File '+str(
313                filename)+' is a binary file. Do you want to read this file?'
314        # it would be better to use something that
315        # would resize better, but this will do for now
316        dlg = wx.MessageDialog(
317            self, rdmsg,
318            'Is this the file you want?', 
319            wx.YES_NO | wx.ICON_QUESTION,
320            )
321        dlg.SetSize((700,300)) # does not resize on Mac
322        result = wx.ID_NO
323        try:
324            result = dlg.ShowModal()
325        finally:
326            dlg.Destroy()
327        if result == wx.ID_NO: return True
328        return False
329   
330    def OnImportGeneric(self,reader,readerlist,label,multiple=False,
331                        usedRanIdList=[],Preview=True):
332        '''Used to import Phases or datasets using reader objects
333        subclassed from :class:`GSASIIIO.ImportPhase`,
334        :class:`GSASIIIO.ImportStructFactor`,
335        :class:`GSASIIIO.ImportPowderData`,
336        :class:`GSASIIIO.ImportSmallAngleData` or
337        :class:`GSASIIIO.ImportImage`.
338        If a specific reader is specified, only that method will be called,
339        but if no reader is specified, every one that is potentially
340        compatible (by file extension) will be tried on the file(s)
341        selected in the Open File dialog.
342
343        :param readerobject reader: This will be a reference to
344          a particular object to be used to read a file or None,
345          if every appropriate reader should be used.
346
347        :param list readerlist: a list of reader objects appropriate for
348          the current read attempt. At present, this will be either
349          self.ImportPhaseReaderlist, self.ImportSfactReaderlist or
350          self.ImportPowderReaderlist (defined in _init_Imports from
351          the files found in the path), but in theory this list could
352          be tailored. Used only when reader is None.
353
354        :param str label: string to place on the open file dialog:
355          Open `label` input file
356
357        :param bool multiple: True if multiple files can be selected
358          in the file dialog. False is default. At present True is used
359          only for reading of powder data.
360
361        :param list usedRanIdList: an optional list of random Ids that
362          have been used and should not be reused
363
364        :param bool Preview: indicates if a preview of the file should
365          be shown. Default is True, but set to False for image files
366          which are all binary.
367
368        :returns: a list of reader objects (rd_list) that were able
369          to read the specified file(s). This list may be empty.
370        '''
371        self.lastimport = ''
372        self.zipfile = None
373        singlereader = True
374        if reader is None:
375            singlereader = False
376            multiple = False
377            #print "use all formats"
378            choices = "any file (*.*)|*.*"
379            choices += "|zip archive (.zip)|*.zip"
380            extdict = {}
381            # compile a list of allowed extensions
382            for rd in readerlist:
383                fmt = rd.formatName
384                for extn in rd.extensionlist:
385                    if not extdict.get(extn): extdict[extn] = []
386                    extdict[extn] += [fmt,]
387            for extn in sorted(extdict.keys(),cmp=lambda x,y: cmp(x.lower(), y.lower())):
388                fmt = ''
389                for f in extdict[extn]:
390                    if fmt != "": fmt += ', '
391                    fmt += f
392                choices += "|" + fmt + " file (*" + extn + ")|*" + extn
393        else:
394            readerlist = [reader,]
395            # compile a list of allowed extensions
396            choices = reader.formatName + " file ("
397            w = ""
398            for extn in reader.extensionlist:
399                if w != "": w += ";"
400                w += "*" + extn
401            choices += w + ")|" + w
402            choices += "|zip archive (.zip)|*.zip"
403            if not reader.strictExtension:
404                choices += "|any file (*.*)|*.*"
405        # get the file(s) (probably should remove wx.CHANGE_DIR here)
406        if multiple:
407            mode = style=wx.OPEN | wx.CHANGE_DIR | wx.MULTIPLE
408        else:
409            mode = style=wx.OPEN | wx.CHANGE_DIR
410        filelist = self.GetImportFile(message="Choose "+label+" input file",
411                    defaultFile="",wildcard=choices, style=mode)
412        rd_list = []
413        filelist1 = []
414        for filename in filelist:
415            # is this a zip file?
416            if os.path.splitext(filename)[1].lower() == '.zip':
417                extractedfiles = G2IO.ExtractFileFromZip(
418                    filename,parent=self,
419                    multipleselect=True)
420                if extractedfiles is None: continue # error or Cancel
421                if extractedfiles != filename:
422                    self.zipfile = filename # save zip name
423                    filelist1 += extractedfiles
424                    continue
425            filelist1.append(filename)
426        filelist = filelist1
427        for filename in filelist:
428            # is this a zip file?
429            if os.path.splitext(filename)[1].lower() == '.zip':
430                extractedfile = G2IO.ExtractFileFromZip(filename,parent=self)
431                if extractedfile is None: continue # error or Cancel
432                if extractedfile != filename:
433                    filename,self.zipfile = extractedfile,filename # now use the file that was created
434            # determine which formats are compatible with this file
435            primaryReaders = []
436            secondaryReaders = []
437            for rd in readerlist:
438                flag = rd.ExtensionValidator(filename)
439                if flag is None: 
440                    secondaryReaders.append(rd)
441                elif flag:
442                    primaryReaders.append(rd)
443            if len(secondaryReaders) + len(primaryReaders) == 0 and reader:
444                self.ErrorDialog('Not supported','The selected reader cannot read file '+filename)
445                return []
446            elif len(secondaryReaders) + len(primaryReaders) == 0:
447                self.ErrorDialog('No Format','No matching format for file '+filename)
448                return []
449
450            fp = None
451            msg = ''
452            fp = open(filename,'Ur')
453            if len(filelist) == 1 and Preview:
454                if self.PreviewFile(filename,fp): return []
455            self.lastimport = filename # this is probably not what I want to do -- it saves only the
456            # last name in a series. See rd.readfilename for a better name.
457
458            # try the file first with Readers that specify the
459            # file's extension and later with ones that merely allow it
460            errorReport = ''
461            for rd in primaryReaders+secondaryReaders:
462                rd.ReInitialize() # purge anything from a previous read
463                fp.seek(0)  # rewind
464                rd.errors = "" # clear out any old errors
465                if not rd.ContentsValidator(fp): # rejected on cursory check
466                    errorReport += "\n  "+rd.formatName + ' validator error'
467                    if rd.errors: 
468                        errorReport += ': '+rd.errors
469                    continue 
470                repeat = True
471                rdbuffer = {} # create temporary storage for file reader
472                block = 0
473                while repeat: # loop if the reader asks for another pass on the file
474                    block += 1
475                    repeat = False
476                    fp.seek(0)  # rewind
477                    rd.objname = os.path.basename(filename)
478                    flag = False
479                    if GSASIIpath.GetConfigValue('debug'):
480                        flag = rd.Reader(filename,fp,self,
481                                         buffer=rdbuffer,
482                                         blocknum=block,
483                                         usedRanIdList=usedRanIdList,
484                                         )
485                    else:
486                        try:
487                            flag = rd.Reader(filename,fp,self,
488                                             buffer=rdbuffer,
489                                             blocknum=block,
490                                             usedRanIdList=usedRanIdList,
491                                             )
492                        except rd.ImportException as detail:
493                            rd.errors += "\n  Read exception: "+str(detail)
494                        except Exception as detail:
495                            import traceback
496                            rd.errors += "\n  Unhandled read exception: "+str(detail)
497                            rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
498                    if flag: # this read succeeded
499                        rd.readfilename = filename
500                        rd_list.append(copy.deepcopy(rd)) # save the result before it is written over
501                        if rd.repeat:
502                            repeat = True
503                        continue
504                    errorReport += '\n'+rd.formatName + ' read error'
505                    if rd.errors:
506                        errorReport += ': '+rd.errors
507                if rd_list: # read succeeded, was there a warning or any errors?
508                    if rd.warnings:
509                        self.ErrorDialog('Read Warning','The '+ rd.formatName+
510                                         ' reader reported a warning message:\n\n'+
511                                         rd.warnings)
512                    break # success in reading, try no further
513            else:
514                if singlereader:
515                    self.ErrorDialog('Read Error','The '+ rd.formatName+
516                                     ' reader was not able to read file '+filename+msg+
517                                     '\n\nError message(s):\n'+errorReport
518                                     )
519                else:
520                    self.ErrorDialog('Read Error','No reader was able to read file '+filename+msg+
521                                     '\n\nError messages:\n'+errorReport
522                                     )
523            if fp: fp.close()
524        return rd_list
525
526    def _Add_ImportMenu_Phase(self,parent):
527        '''configure the Import Phase menus accord to the readers found in _init_Imports
528        '''
529        submenu = wx.Menu()
530        item = parent.AppendMenu(wx.ID_ANY, 'Phase',
531            submenu, help='Import phase data')
532        for reader in self.ImportPhaseReaderlist:
533            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
534                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
535            self.ImportMenuId[item.GetId()] = reader
536            self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
537        item = submenu.Append(wx.ID_ANY,
538                              help='Import phase data, use file to try to determine format',
539                              kind=wx.ITEM_NORMAL,
540                              text='guess format from file')
541        self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
542
543    def GetImportFile(self, message, defaultDir="", defaultFile="", style=wx.OPEN,
544                      *args, **kwargs):
545        '''Defines a customized dialog that gets gets files from the appropriate
546        Import directory (unless overridden with defaultDir). The default
547        import directory is found in self.ImportDir, which will be set to
548        the location of the (first) file entered in this dialog.
549           
550        :returns: a list of files or an empty list
551        '''
552        dlg = wx.FileDialog(self, message, defaultDir, defaultFile, *args,
553                            style=style, **kwargs)
554        if not defaultDir and self.ImportDir:
555            dlg.SetDirectory(self.ImportDir)
556        try:
557            if dlg.ShowModal() == wx.ID_OK:
558                if style & wx.MULTIPLE:
559                    filelist = dlg.GetPaths()
560                    if len(filelist) == 0: return []
561                else:
562                    filelist = [dlg.GetPath(),]
563                # not sure if we want to do this (why use wx.CHANGE_DIR?)
564                if style & wx.CHANGE_DIR: # to get Mac/Linux to change directory like windows!
565                    os.chdir(dlg.GetDirectory())
566                if not defaultDir and self.ImportDir: # if we are using a default and
567                    # user moved, save the new location
568                    self.ImportDir = os.path.split(filelist[0])[0]
569                    #print 'default moved to',self.ImportDir               
570            else: # cancel was pressed
571                return []
572        finally:
573            dlg.Destroy()
574        return filelist           
575       
576    def OnImportPhase(self,event):
577        '''Called in response to an Import/Phase/... menu item
578        to read phase information.
579        dict self.ImportMenuId is used to look up the specific
580        reader item associated with the menu item, which will be
581        None for the last menu item, which is the "guess" option
582        where all appropriate formats will be tried.
583        '''
584        # look up which format was requested
585        reqrdr = self.ImportMenuId.get(event.GetId())
586       
587        # make a list of phase names, ranId's and the histograms used in those phases
588        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
589        phaseNameList = usedHistograms.keys() # phase names in use
590        usedHKLFhists = [] # used single-crystal histograms
591        for p in usedHistograms:
592            for h in usedHistograms[p]:
593                if h.startswith('HKLF ') and h not in usedHKLFhists:
594                    usedHKLFhists.append(h)
595        rdlist = self.OnImportGeneric(reqrdr,
596                                  self.ImportPhaseReaderlist,
597                                  'phase',usedRanIdList=phaseRIdList)
598        if len(rdlist) == 0: return
599        # for now rdlist is only expected to have one element
600        # but below will allow multiple phases to be imported
601        # if ever the import routines ever implement multiple phase reads.
602        self.CheckNotebook()
603        newPhaseList = []
604        for rd in rdlist:
605            PhaseName = ''
606            dlg = wx.TextEntryDialog( # allow editing of phase name
607                                    self, 'Enter the name for the new phase',
608                                    'Edit phase name', rd.Phase['General']['Name'],
609                                    style=wx.OK)
610            while PhaseName == '':
611                dlg.CenterOnParent()
612                if dlg.ShowModal() == wx.ID_OK:
613                    PhaseName = dlg.GetValue().strip()
614                else:
615                    dlg.Destroy()
616                    return
617            dlg.Destroy()
618            # make new phase names unique
619            rd.Phase['General']['Name'] = G2obj.MakeUniqueLabel(PhaseName,phaseNameList)
620            PhaseName = rd.Phase['General']['Name'][:]
621            newPhaseList.append(PhaseName)
622            print 'Read phase '+str(PhaseName)+' from file '+str(self.lastimport)
623            if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
624                sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
625            else:
626                sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
627            psub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
628            self.PatternTree.SetItemPyData(psub,rd.Phase)
629            self.PatternTree.Expand(self.root) # make sure phases are seen
630            self.PatternTree.Expand(sub) 
631            self.PatternTree.Expand(psub)
632            self.PatternTree.SelectItem(psub) # show the page to complete the initialization (yuk!)
633            wx.Yield() # make sure call of GSASII.OnPatternTreeSelChanged happens before we go on
634
635            if rd.Constraints:
636                sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') # was created in CheckNotebook if needed
637                Constraints = self.PatternTree.GetItemPyData(sub)               
638                # TODO: make sure that NEWVAR names are unique here?
639                for i in rd.Constraints:
640                    if type(i) is dict:
641                        #for j in i: print j,' --> ',i[j]
642                        if '_Explain' not in Constraints: Constraints['_Explain'] = {}
643                        Constraints['_Explain'].update(i)
644                        continue
645                    Constraints['Phase'].append(i)
646        if not newPhaseList: return # somehow, no new phases
647        # get a list of existing histograms
648        PWDRlist = []
649        HKLFlist = []
650        if self.PatternTree.GetCount():
651            item, cookie = self.PatternTree.GetFirstChild(self.root)
652            while item:
653                name = self.PatternTree.GetItemText(item)
654                if name.startswith('PWDR ') and name not in PWDRlist:
655                    PWDRlist.append(name)
656                if name.startswith('HKLF ') and name not in HKLFlist:
657                    HKLFlist.append(name)
658                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
659        TextList = PWDRlist + HKLFlist
660        if not len(TextList): return # no data loaded yet
661        header = 'Select histogram(s) to add to new phase(s):'
662        for phaseName in newPhaseList:
663            header += '\n  '+str(phaseName)
664
665        notOK = True
666        while notOK:
667            result = G2G.ItemSelector(TextList,self,header,header='Add histogram(s)',multiple=True)
668            if not result: return
669            # check that selected single crystal histograms are not already in use!
670            used = [TextList[i] for i in result if TextList[i] in usedHKLFhists]
671            #for i in result:
672            #    if TextList[i] in usedHKLFhists: used.append(TextList[i])
673            if used:
674                msg = 'The following single crystal histogram(s) are already in use'
675                for i in used:
676                    msg += '\n  '+str(i)
677                msg += '\nAre you sure you want to add them to this phase? '
678                msg += 'Associating a single crystal dataset to >1 histogram is usually an error, '
679                msg += 'so No is suggested here.'
680                if self.ErrorDialog('Likely error',msg,self,wtype=wx.YES_NO) == wx.ID_YES: notOK = False
681            else:
682                notOK = False
683        # connect new phases to histograms
684        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
685        if not sub:
686            raise Exception('ERROR -- why are there no phases here?')
687        wx.BeginBusyCursor()
688        item, cookie = self.PatternTree.GetFirstChild(sub)
689        while item: # loop over (new) phases
690            phaseName = self.PatternTree.GetItemText(item)
691            data = self.PatternTree.GetItemPyData(item)
692            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
693            if phaseName not in newPhaseList: continue
694            generalData = data['General']
695            SGData = generalData['SGData']
696            Super = generalData.get('Super',0)
697            SuperVec = []
698            if Super:
699                SuperVec = np.array(generalData['SuperVec'][0])
700            UseList = data['Histograms']
701            NShkl = len(G2spc.MustrainNames(SGData))
702            NDij = len(G2spc.HStrainNames(SGData))
703            for i in result:
704                histoName = TextList[i]
705                if histoName in HKLFlist:
706                    #redo UpdateHKLFdata(histoName) here:
707                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
708                    refDict,reflData = self.PatternTree.GetItemPyData(Id)
709                    G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
710                    Super = reflData.get('Super',0)
711                    for iref,ref in enumerate(reflData['RefList']):
712                        hkl = ref[:3]
713                        if Super:
714                            H = list(hkl+SuperVec*ref[3])
715                        else:
716                            H = hkl
717                        ref[4+Super] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
718                        iabsnt = G2spc.GenHKLf(H,SGData)[0]
719                        if iabsnt:  #flag space gp. absences
720                            if Super: 
721                                if not ref[2+Super]:
722                                    ref[3+Super] = 0
723                                else:
724                                    ref[3+Super] = 1    #twin id?
725                            else:
726                                ref[3] = 0
727                    UseList[histoName] = SetDefaultDData(reflData['Type'],histoName)
728                elif histoName in PWDRlist:
729                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
730                    refList = self.PatternTree.GetItemPyData(
731                        G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
732                    refList[generalData['Name']] = {}
733                    UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
734                else:
735                    raise Exception('Unexpected histogram '+str(histoName))
736        wx.EndBusyCursor()
737        return # success
738       
739    def _Add_ImportMenu_Image(self,parent):
740        '''configure the Import Image menus accord to the readers found in _init_Imports
741        '''
742        submenu = wx.Menu()
743        item = parent.AppendMenu(wx.ID_ANY, 'Image',
744            submenu, help='Import image file')
745        for reader in self.ImportImageReaderlist:
746            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
747                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
748            self.ImportMenuId[item.GetId()] = reader
749            self.Bind(wx.EVT_MENU, self.OnImportImage, id=item.GetId())
750        item = submenu.Append(wx.ID_ANY,
751                              help='Import image data, use file to try to determine format',
752                              kind=wx.ITEM_NORMAL,
753                              text='guess format from file')
754        self.Bind(wx.EVT_MENU, self.OnImportImage, id=item.GetId())
755       
756    def OnImportImage(self,event):
757        '''Called in response to an Import/Image/... menu item
758        to read an image from a file. Like all the other imports,
759        dict self.ImportMenuId is used to look up the specific
760        reader item associated with the menu item, which will be
761        None for the last menu item, which is the "guess" option
762        where all appropriate formats will be tried.
763
764        A reader object is filled each time an image is read.
765        '''
766        # look up which format was requested
767        reqrdr = self.ImportMenuId.get(event.GetId())
768        rdlist = self.OnImportGeneric(reqrdr,
769                                  self.ImportImageReaderlist,
770                                  'image',multiple=True,Preview=False)
771        if not rdlist: return
772        first = True
773        for rd in rdlist:
774            if first:
775                first = False
776                self.CheckNotebook()
777            G2IO.LoadImage2Tree(rd.readfilename,self,rd.Comments,rd.Data,rd.Npix,rd.Image)
778        self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,self.Image,'Image Controls'))             #show last image to have beeen read
779                   
780    def _Add_ImportMenu_Sfact(self,parent):
781        '''configure the Import Structure Factor menus accord to the readers found in _init_Imports
782        '''
783        submenu = wx.Menu()
784        item = parent.AppendMenu(wx.ID_ANY, 'Structure Factor',
785            submenu, help='Import Structure Factor data')
786        for reader in self.ImportSfactReaderlist:
787            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,               
788                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
789            self.ImportMenuId[item.GetId()] = reader
790            self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
791        item = submenu.Append(wx.ID_ANY,
792            help='Import Structure Factor, use file to try to determine format',
793            kind=wx.ITEM_NORMAL,
794            text='guess format from file')
795        self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
796
797    def OnImportSfact(self,event):
798        '''Called in response to an Import/Structure Factor/... menu item
799        to read single crystal datasets.
800        dict self.ImportMenuId is used to look up the specific
801        reader item associated with the menu item, which will be
802        None for the last menu item, which is the "guess" option
803        where all appropriate formats will be tried.
804        '''
805        # get a list of existing histograms
806        HKLFlist = []
807        if self.PatternTree.GetCount():
808            item, cookie = self.PatternTree.GetFirstChild(self.root)
809            while item:
810                name = self.PatternTree.GetItemText(item)
811                if name.startswith('HKLF ') and name not in HKLFlist:
812                    HKLFlist.append(name)
813                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
814        # look up which format was requested
815        reqrdr = self.ImportMenuId.get(event.GetId())
816        rdlist = self.OnImportGeneric(reqrdr,self.ImportSfactReaderlist,
817            'Structure Factor',multiple=True)
818        if len(rdlist) == 0: return
819        self.CheckNotebook()
820        newHistList = []
821        for rd in rdlist:
822            HistName = rd.objname
823            if len(rdlist) <= 2: 
824                dlg = wx.TextEntryDialog( # allow editing of Structure Factor name
825                    self, 'Enter the name for the new Structure Factor',
826                    'Edit Structure Factor name', HistName,
827                    style=wx.OK)
828                dlg.CenterOnParent()
829                if dlg.ShowModal() == wx.ID_OK:
830                    HistName = dlg.GetValue()
831                dlg.Destroy()
832            HistName = 'HKLF '+HistName
833            # make new histogram names unique
834            if len(rd.Banks):
835                for Bank in rd.Banks:
836                    valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
837                    HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
838                    print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
839                    Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
840                    if not Bank['RefDict'].get('FF'):
841                        Bank['RefDict']['FF'] = {}
842                    self.PatternTree.SetItemPyData(Id,[valuesdict,Bank['RefDict']])
843                    Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
844                    self.PatternTree.SetItemPyData(Sub,copy.copy(rd.Parameters))
845                    self.PatternTree.SetItemPyData(
846                        self.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
847                    newHistList.append(HistName)
848            else:
849                valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
850                HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
851                print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
852                if not rd.RefDict.get('FF'):
853                    rd.RefDict['FF'] = {}
854                Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
855                self.PatternTree.SetItemPyData(Id,[valuesdict,rd.RefDict])
856                Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
857                self.PatternTree.SetItemPyData(Sub,rd.Parameters)
858                self.PatternTree.SetItemPyData(
859                    self.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
860                newHistList.append(HistName)
861               
862            self.PatternTree.SelectItem(Id)
863            self.PatternTree.Expand(Id)
864            self.Sngl = True
865
866        if not newHistList: return # somehow, no new histograms
867        # make a list of phase names
868        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
869        phaseNameList = usedHistograms.keys() # phase names in use
870        if not phaseNameList: return # no phases yet, nothing to do
871        header = 'Select phase(s) to add the new\nsingle crystal dataset(s) to:'
872        for Name in newHistList:
873            header += '\n  '+str(Name)
874        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
875        if not result: return
876        # connect new phases to histograms
877        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
878        if not sub:
879            raise Exception('ERROR -- why are there no phases here?')
880        wx.BeginBusyCursor()
881        item, cookie = self.PatternTree.GetFirstChild(sub)
882        iph = -1
883        while item: # loop over (new) phases
884            iph += 1
885            phaseName = self.PatternTree.GetItemText(item)
886            data = self.PatternTree.GetItemPyData(item)
887            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
888            if iph not in result: continue
889            generalData = data['General']
890            SGData = generalData['SGData']
891            Super = generalData.get('Super',0)
892            SuperVec = []
893            if Super:
894                SuperVec = np.array(generalData['SuperVec'][0])
895            UseList = data['Histograms']
896            for histoName in newHistList:
897                #redo UpdateHKLFdata(histoName) here:
898                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
899                refDict,reflData = self.PatternTree.GetItemPyData(Id)
900                UseList[histoName] = SetDefaultDData(reflData['Type'],histoName)
901                G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
902                if 'TwMax' in reflData:     #nonmerohedral twins present
903                    UseList[histoName]['Twins'] = []
904                    for iT in range(reflData['TwMax'][0]+1):
905                        if iT in reflData['TwMax'][1]:
906                            UseList[histoName]['Twins'].append([False,0.0])
907                        else:
908                            UseList[histoName]['Twins'].append([np.array([[1,0,0],[0,1,0],[0,0,1]]),[1.0,False,reflData['TwMax'][0]]])
909                else:   #no nonmerohedral twins
910                    UseList[histoName]['Twins'] = [[np.array([[1,0,0],[0,1,0],[0,0,1]]),[1.0,False,0]],]
911                for iref,ref in enumerate(reflData['RefList']):
912                    hkl = ref[:3]
913                    if Super:
914                        H = list(hkl+SuperVec*ref[3])
915                    else:
916                        H = hkl
917                    ref[4+Super] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
918                    iabsnt,mul,Uniq,phi = G2spc.GenHKLf(H,SGData)
919                    if iabsnt:  #flag space gp. absences
920                        if Super: 
921                            if not ref[2+Super]:
922                                ref[3+Super] = 0
923                            else:
924                                ref[3+Super] = 1    #twin id?
925                        else:
926                            ref[3] = 0
927        wx.EndBusyCursor()
928       
929        return # success
930
931    def _Add_ImportMenu_powder(self,parent):
932        '''configure the Powder Data menus accord to the readers found in _init_Imports
933        '''
934        submenu = wx.Menu()
935        item = parent.AppendMenu(wx.ID_ANY, 'Powder Data',
936            submenu, help='Import Powder data')
937        for reader in self.ImportPowderReaderlist:
938            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
939                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
940            self.ImportMenuId[item.GetId()] = reader
941            self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
942        item = submenu.Append(wx.ID_ANY,
943            help='Import powder data, use file to try to determine format',
944            kind=wx.ITEM_NORMAL,text='guess format from file')
945        self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
946        submenu.AppendSeparator()
947        item = submenu.Append(wx.ID_ANY,
948            help='Create a powder data set entry that will be simulated',
949            kind=wx.ITEM_NORMAL,text='Simulate a dataset')
950        self.Bind(wx.EVT_MENU, self.OnDummyPowder, id=item.GetId())
951       
952    def OpenPowderInstprm(self,instfile):
953        '''Read a GSAS-II (new) instrument parameter file
954
955        :param str instfile: name of instrument parameter file
956
957        '''
958        if os.path.splitext(instfile)[1].lower() != '.instprm': # invalid file
959            return None           
960        if not os.path.exists(instfile): # no such file
961            return None
962        File = open(instfile,'r')
963        lines = File.readlines()
964        File.close()
965        return lines       
966           
967    def ReadPowderInstprm(self,instLines):
968        '''Read lines from a GSAS-II (new) instrument parameter file
969
970        :param list instLines: strings from GSAS-II parameter file; can be concatenated with ';'
971
972        '''
973        if not instLines[0].startswith('#GSAS-II'): # not a valid file
974            return None
975        newItems = []
976        newVals = []
977        for S in instLines:
978            if S[0] == '#':
979                continue
980            S = S.replace(' ','')
981            SS = S[:-1].split(';')
982            for s in SS:
983                [item,val] = s.split(':')
984                newItems.append(item)
985                try:
986                    newVals.append(float(val))
987                except ValueError:
988                    newVals.append(val)                       
989        return G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}
990       
991    def ReadPowderIparm(self,instfile,bank,databanks,rd):
992        '''Read a GSAS (old) instrument parameter file
993
994        :param str instfile: name of instrument parameter file
995
996        :param int bank: the bank number read in the raw data file
997
998        :param int databanks: the number of banks in the raw data file.
999          If the number of banks in the data and instrument parameter files
1000          agree, then the sets of banks are assumed to match up and bank
1001          is used to select the instrument parameter file. If not, the user
1002          is asked to make a selection.
1003
1004        :param obj rd: the raw data (histogram) data object. This
1005          sets rd.instbank.
1006
1007        '''
1008        if not os.path.exists(instfile): # no such file
1009            return {}
1010        fp = 0
1011        try:
1012            fp = open(instfile,'Ur')
1013            Iparm = {}
1014            for S in fp:
1015                if '#' in S[0]:
1016                    continue
1017                Iparm[S[:12]] = S[12:-1]
1018        except IOError:
1019            print('Error reading file:'+str(instfile))
1020        if fp:       
1021            fp.close()
1022
1023        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
1024        hType = Iparm['INS   HTYPE '].strip()
1025        if ibanks == 1: # there is only one bank here, return it
1026            rd.instbank = 1
1027            return Iparm
1028        if 'PNT' in hType:
1029            rd.instbank = bank
1030        elif ibanks != databanks:
1031            # number of banks in data and prm file not not agree, need a
1032            # choice from a human here
1033            choices = []
1034            for i in range(1,1+ibanks):
1035                choices.append('Bank '+str(i))
1036            bank = rd.BlockSelector(
1037                choices, self,
1038                title='Select an instrument parameter bank for '+
1039                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
1040                '\nOr use Cancel to select from the default parameter sets',
1041                header='Block Selector')
1042        if bank is None: return {}
1043        # pull out requested bank # bank from the data, and change the bank to 1
1044        IparmS = {}
1045        for key in Iparm:
1046            if 'INS' in key[:3]:    #skip around rubbish lines in some old iparm files
1047                if key[4:6] == "  ":
1048                    IparmS[key] = Iparm[key]
1049                elif int(key[4:6].strip()) == bank:
1050                    IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
1051        rd.instbank = bank
1052        return IparmS
1053                       
1054    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
1055        '''Open and read an instrument parameter file for a data file
1056        Returns the list of parameters used in the data tree
1057
1058        :param obj rd: the raw data (histogram) data object.
1059
1060        :param str prevIparm: not used
1061
1062        :param str lastIparmfile: Name of last instrument parameter
1063          file that was read, or a empty string.
1064
1065        :param str lastdatafile: Name of last data file that was read.
1066
1067        :returns: a list of two dicts, the first containing instrument parameters
1068          and the second used for TOf lookup tables for profile coeff.
1069
1070        '''
1071        def SetPowderInstParms(Iparm, rd):
1072            '''extracts values from instrument parameters in rd.instdict
1073            or in array Iparm.
1074            Create and return the contents of the instrument parameter tree entry.
1075            '''
1076            Irads = {0:' ',1:'CrKa',2:'FeKa',3:'CuKa',4:'MoKa',5:'AgKa',6:'TiKa',7:'CoKa'}
1077            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
1078            # override inst values with values read from data file
1079            Bank = rd.powderentry[2]    #should be used in multibank iparm files
1080            if rd.instdict.get('type'):
1081                DataType = rd.instdict.get('type')
1082            data = [DataType,]
1083            instname = Iparm.get('INS  1INAME ')
1084            irad = int(Iparm.get('INS  1 IRAD ','0'))
1085            if instname:
1086                rd.Sample['InstrName'] = instname.strip()
1087            if 'C' in DataType:
1088                wave1 = None
1089                wave2 = 0.0
1090                if rd.instdict.get('wave'):
1091                    wl = rd.instdict.get('wave')
1092                    wave1 = wl[0]
1093                    if len(wl) > 1: wave2 = wl[1]
1094                s = Iparm['INS  1 ICONS']
1095                if not wave1:
1096                    wave1 = G2IO.sfloat(s[:10])
1097                    wave2 = G2IO.sfloat(s[10:20])
1098                v = (wave1,wave2,
1099                     G2IO.sfloat(s[20:30]),G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
1100                if not v[1]:
1101                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
1102                    v = (v[0],v[2],v[4])
1103                    codes = [0,0,0,0]
1104                else:
1105                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
1106                    codes = [0,0,0,0,0,0]
1107                data.extend(v)
1108                if 'INS  1PRCF  ' in Iparm:
1109                    v1 = Iparm['INS  1PRCF  '].split()                                                 
1110                    v = Iparm['INS  1PRCF 1'].split()
1111                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1112                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1113                    v = Iparm['INS  1PRCF 2'].split()
1114                    if v1[0] == 3:
1115                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1116                    else:
1117                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file                   
1118                else:
1119                    v1 = Iparm['INS  1PRCF1 '].split()                                                 
1120                    v = Iparm['INS  1PRCF11'].split()
1121                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1122                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1123                    v = Iparm['INS  1PRCF12'].split()
1124                    if v1[0] == 3:
1125                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1126                    else:
1127                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
1128                codes.extend([0,0,0,0,0,0,0])
1129                Iparm1 = G2IO.makeInstDict(names,data,codes)
1130                Iparm1['Source'] = [Irads[irad],Irads[irad]]
1131                return [Iparm1,{}]
1132            elif 'T' in DataType:
1133                names = ['Type','fltPath','2-theta','difC','difA', 'difB','Zero','alpha','beta-0','beta-1',
1134                    'beta-q','sig-0','sig-1','sig-2','sig-q', 'X','Y','Azimuth',]
1135                codes = [0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,]
1136                azm = 0.
1137                if 'INS  1DETAZM' in Iparm:
1138                    azm = float(Iparm['INS  1DETAZM'])
1139                s = Iparm['INS   FPATH1'].split()
1140                fltPath0 = G2IO.sfloat(s[0])
1141                s = Iparm['INS  1BNKPAR'].split()
1142                fltPath1 = G2IO.sfloat(s[0])
1143                data.extend([fltPath0+fltPath1,])               #Flight path source-sample-detector
1144                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
1145                s = Iparm['INS  1 ICONS'].split()
1146                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,G2IO.sfloat(s[2])])    #difC,difA,difB,Zero
1147                if 'INS  1PRCF  ' in Iparm:
1148                    s = Iparm['INS  1PRCF  '].split()
1149                    pfType = int(s[0])
1150                    s = Iparm['INS  1PRCF 1'].split()
1151                    if abs(pfType) == 1:
1152                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1153                        s = Iparm['INS  1PRCF 2'].split()
1154                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1155                    elif abs(pfType) in [3,4,5]:
1156                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1157                        if abs(pfType) == 4:
1158                            data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1159                        else:
1160                            s = Iparm['INS  1PRCF 2'].split()
1161                            data.extend([0.0,0.0,G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                       
1162                else:
1163                    s = Iparm['INS  1PRCF1 '].split()
1164                    pfType = int(s[0])
1165                    s = Iparm['INS  1PRCF11'].split()
1166                    if abs(pfType) == 1:
1167                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1168                        s = Iparm['INS  1PRCF12'].split()
1169                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1170                    elif abs(pfType) in [3,4,5]:
1171                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1172                        if abs(pfType) == 4:
1173                            data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1174                        else:
1175                            s = Iparm['INS  1PRCF12'].split()
1176                            data.extend([0.0,0.0,G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                       
1177                Inst1 = G2IO.makeInstDict(names,data,codes)
1178                Inst2 = {}
1179                if pfType < 0:
1180                    Ipab = 'INS  1PAB'+str(-pfType)
1181                    Npab = int(Iparm[Ipab+'  '].strip())
1182                    Inst2['Pdabc'] = []
1183                    for i in range(Npab):
1184                        k = Ipab+str(i+1).rjust(2)
1185                        s = Iparm[k].split()
1186                        Inst2['Pdabc'].append([float(t) for t in s])
1187                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
1188                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
1189                if 'INS  1I ITYP' in Iparm:
1190                    s = Iparm['INS  1I ITYP'].split()
1191                    Ityp = int(s[0])
1192                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
1193                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
1194                    if Ityp in [1,2,4]:
1195                        Inst2['Itype'] = Itypes[Ityp-1]
1196                        Inst2['Tminmax'] = Tminmax
1197                        Icoeff = []
1198                        Iesd = []
1199                        Icovar = []                   
1200                        for i in range(3):
1201                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
1202                            Icoeff += [float(S) for S in s]
1203                            s = Iparm['INS  1IECOF'+str(i+1)].split()
1204                            Iesd += [float(S) for S in s]
1205                        NT = 10
1206                        for i in range(8):
1207                            s = Iparm['INS  1IECOR'+str(i+1)]
1208                            if i == 7:
1209                                NT = 8
1210                            Icovar += [float(s[6*j:6*j+6]) for j in range(NT)]
1211                        Inst2['Icoeff'] = Icoeff
1212                        Inst2['Iesd'] = Iesd
1213                        Inst2['Icovar'] = Icovar
1214                return [Inst1,Inst2]
1215
1216        # stuff we might need from the reader
1217        filename = rd.powderentry[0]
1218        bank = rd.powderentry[2]
1219        numbanks = rd.numbanks
1220        # is there an instrument parameter file defined for the current data set?
1221        # or if this is a read on a set of set of files, use the last one again
1222        #if rd.instparm or (lastdatafile == filename and lastIparmfile):
1223        if rd.instparm or lastIparmfile:
1224            if rd.instparm:
1225                instfile = os.path.join(os.path.split(filename)[0],
1226                                    rd.instparm)
1227            else:
1228                # for multiple reads of one data file, reuse the inst parm file
1229                instfile = lastIparmfile
1230            if os.path.exists(instfile):
1231                #print 'debug: try read',instfile
1232                Lines = self.OpenPowderInstprm(instfile)
1233                instParmList = None
1234                if Lines is not None:
1235                    instParmList = self.ReadPowderInstprm(Lines)
1236                if instParmList is not None:
1237                    rd.instfile = instfile
1238                    rd.instmsg = 'GSAS-II file '+instfile
1239                    return instParmList
1240                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1241                if Iparm:
1242                    #print 'debug: success'
1243                    rd.instfile = instfile
1244                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1245                    return SetPowderInstParms(Iparm,rd)
1246            else:
1247                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1248                    +str(instfile)+' requested by file '+ filename)
1249        # is there an instrument parameter file matching the current file
1250        # with extension .inst or .prm? If so read it
1251        basename = os.path.splitext(filename)[0]
1252        for ext in '.instprm','.prm','.inst','.ins':
1253            instfile = basename + ext
1254            Lines = self.OpenPowderInstprm(instfile)
1255            instParmList = None
1256            if Lines is not None:
1257                instParmList = self.ReadPowderInstprm(Lines)
1258            if instParmList is not None:
1259                rd.instfile = instfile
1260                rd.instmsg = 'GSAS-II file '+instfile
1261                return instParmList
1262            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1263            if Iparm:
1264                #print 'debug: success'
1265                rd.instfile = instfile
1266                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1267                return SetPowderInstParms(Iparm,rd)
1268            else:
1269                #print 'debug: open/read failed',instfile
1270                pass # fail silently
1271
1272        # did we read the data file from a zip? If so, look there for a
1273        # instrument parameter file
1274        if self.zipfile:
1275            for ext in '.instprm','.prm','.inst','.ins':
1276                instfile = G2IO.ExtractFileFromZip(
1277                    self.zipfile,
1278                    selection=os.path.split(basename + ext)[1],
1279                    parent=self)
1280                if instfile is not None and instfile != self.zipfile:
1281                    print 'debug:',instfile,'created from ',self.zipfile
1282                    Lines = self.OpenPowderInstprm(instfile)
1283                    instParmList = None
1284                    if Lines is not None:
1285                        instParmList = self.ReadPowderInstprm(Lines)
1286                    if instParmList is not None:
1287                        rd.instfile = instfile
1288                        rd.instmsg = 'GSAS-II file '+instfile
1289                        return instParmList
1290                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1291                    if Iparm:
1292                        rd.instfile = instfile
1293                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1294                        return SetPowderInstParms(Iparm,rd)
1295                    else:
1296                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
1297                        pass # fail silently
1298
1299        while True: # loop until we get a file that works or we get a cancel
1300            instfile = ''
1301            dlg = wx.FileDialog(
1302                self,
1303                'Choose inst. param file for "'
1304                +rd.idstring
1305                +'" (or Cancel for default)',
1306                '.', '',
1307                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1308                'GSAS-II iparm file (*.instprm)|*.instprm|'
1309                'All files (*.*)|*.*', 
1310                wx.OPEN|wx.CHANGE_DIR)
1311            if os.path.exists(lastIparmfile):
1312                dlg.SetFilename(lastIparmfile)
1313            if dlg.ShowModal() == wx.ID_OK:
1314                instfile = dlg.GetPath()
1315            dlg.Destroy()
1316            if not instfile: break
1317            Lines = self.OpenPowderInstprm(instfile)
1318            instParmList = None
1319            if Lines is not None:
1320                instParmList = self.ReadPowderInstprm(Lines)
1321            if instParmList is not None:
1322                rd.instfile = instfile
1323                rd.instmsg = 'GSAS-II file '+instfile
1324                return instParmList
1325            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1326            if Iparm:
1327                #print 'debug: success with',instfile
1328                rd.instfile = instfile
1329                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1330                return SetPowderInstParms(Iparm,rd)
1331            else:
1332                self.ErrorDialog('Read Error',
1333                                 'Error opening/reading file '+str(instfile))
1334       
1335        # still no success: offer user choice of defaults
1336        import defaultIparms as dI
1337        while True: # loop until we get a choice
1338            choices = []
1339            head = 'Select from default instrument parameters for '+rd.idstring
1340
1341            for l in dI.defaultIparm_lbl:
1342                choices.append('Defaults for '+l)
1343            res = rd.BlockSelector(
1344                choices,
1345                ParentFrame=self,
1346                title=head,
1347                header='Select default inst parms',
1348                useCancel=False)
1349            if res is None: continue
1350            rd.instfile = ''
1351            rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1352            return self.ReadPowderInstprm(dI.defaultIparms[res])
1353
1354    def OnImportPowder(self,event):
1355        '''Called in response to an Import/Powder Data/... menu item
1356        to read a powder diffraction data set.
1357        dict self.ImportMenuId is used to look up the specific
1358        reader item associated with the menu item, which will be
1359        None for the last menu item, which is the "guess" option
1360        where all appropriate formats will be tried.
1361
1362        Also reads an instrument parameter file for each dataset.
1363        '''
1364        # get a list of existing histograms
1365        PWDRlist = []
1366        if self.PatternTree.GetCount():
1367            item, cookie = self.PatternTree.GetFirstChild(self.root)
1368            while item:
1369                name = self.PatternTree.GetItemText(item)
1370                if name.startswith('PWDR ') and name not in PWDRlist:
1371                    PWDRlist.append(name)
1372                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1373        # look up which format was requested
1374        reqrdr = self.ImportMenuId.get(event.GetId()) 
1375        rdlist = self.OnImportGeneric(
1376            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1377        if len(rdlist) == 0: return
1378        self.CheckNotebook()
1379        Iparm = None
1380        lastIparmfile = ''
1381        lastdatafile = ''
1382        newHistList = []
1383        self.EnablePlot = False
1384        for rd in rdlist:
1385            if 'Instrument Parameters' not in rd.pwdparms:
1386                # get instrument parameters for each dataset, unless already set
1387                Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1388                if rd.repeat_instparm: 
1389                    lastIparmfile = rd.instfile
1390                # override any keys in read instrument parameters with ones set in import
1391                for key in Iparm1: 
1392                    if key in rd.instdict:
1393                        Iparm1[key] = rd.instdict[key]
1394            else:
1395                Iparm1,Iparm2 = rd.pwdparms['Instrument Parameters']
1396            lastdatafile = rd.powderentry[0]
1397            HistName = rd.idstring
1398            HistName = 'PWDR '+HistName
1399            # make new histogram names unique
1400            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1401            print 'Read powder data '+str(HistName)+ \
1402                ' from file '+str(rd.readfilename) + \
1403                ' with parameters from '+str(rd.instmsg)
1404            # data are read, now store them in the tree
1405            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1406            if 'T' in Iparm1['Type'][0]:
1407                if not rd.clockWd and rd.GSAS:
1408                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1409                cw = np.diff(rd.powderdata[0])
1410                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1411                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
1412                    npts = min(len(rd.powderdata[0]),len(rd.powderdata[1]),len(cw))
1413                    rd.powderdata[1] = rd.powderdata[1][:npts]/cw[:npts]
1414                    rd.powderdata[2] = rd.powderdata[2][:npts]*cw[:npts]**2  #1/var=w at this point
1415                else:       #NB: from topas/fullprof type files
1416                    rd.powderdata[1] = rd.powderdata[1][:-1]
1417                    rd.powderdata[2] = rd.powderdata[2][:-1]
1418                if 'Itype' in Iparm2:
1419                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1420                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1421                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1422                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1423                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1424                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1425                    var += WYI*rd.powderdata[1]**2
1426                    var /= YI**2
1427                    rd.powderdata[2] = 1./var
1428                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1429                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1430                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1431            valuesdict = {
1432                'wtFactor':1.0,
1433                'Dummy':False,
1434                'ranId':ran.randint(0,sys.maxint),
1435                'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1436                'qPlot':False,'dPlot':False,'sqrtPlot':False
1437                }
1438            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1439            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1440            self.PatternTree.SetItemPyData(
1441                self.PatternTree.AppendItem(Id,text='Comments'),
1442                rd.comments)
1443            Tmin = min(rd.powderdata[0])
1444            Tmax = max(rd.powderdata[0])
1445            self.PatternTree.SetItemPyData(
1446                self.PatternTree.AppendItem(Id,text='Limits'),
1447                rd.pwdparms.get('Limits',[(Tmin,Tmax),[Tmin,Tmax]])
1448                )
1449            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1450            self.PatternTree.SetItemPyData(
1451                self.PatternTree.AppendItem(Id,text='Background'),
1452                rd.pwdparms.get('Background',
1453                    [['chebyschev',True,3,1.0,0.0,0.0],{'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1454                    )
1455            self.PatternTree.SetItemPyData(
1456                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1457                [Iparm1,Iparm2])
1458            self.PatternTree.SetItemPyData(
1459                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1460                rd.Sample)
1461            self.PatternTree.SetItemPyData(
1462                self.PatternTree.AppendItem(Id,text='Peak List')
1463                ,{'peaks':[],'sigDict':{}})
1464            self.PatternTree.SetItemPyData(
1465                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1466                [[],[]])
1467            self.PatternTree.SetItemPyData(
1468                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1469                [])
1470            self.PatternTree.SetItemPyData(
1471                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1472                {})
1473            # if any Control values have been set, move them into tree
1474            Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
1475            Controls.update(rd.Controls)
1476            newHistList.append(HistName)
1477        else:
1478            self.EnablePlot = True
1479            self.PatternTree.Expand(Id)
1480            self.PatternTree.SelectItem(Id)
1481           
1482        if not newHistList: return # somehow, no new histograms
1483        # make a list of phase names
1484        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1485        phaseNameList = usedHistograms.keys() # phase names in use
1486        if not phaseNameList: return # no phases yet, nothing to do
1487        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1488        for Name in newHistList:
1489            header += '\n  '+str(Name)
1490
1491        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1492        if not result: return
1493        # connect new phases to histograms
1494        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1495        if not sub:
1496            raise Exception('ERROR -- why are there no phases here?')
1497        item, cookie = self.PatternTree.GetFirstChild(sub)
1498        iph = -1
1499        while item: # loop over (new) phases
1500            iph += 1
1501            phaseName = self.PatternTree.GetItemText(item)
1502            data = self.PatternTree.GetItemPyData(item)
1503            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1504            if iph not in result: continue
1505            generalData = data['General']
1506            SGData = generalData['SGData']
1507            UseList = data['Histograms']
1508            NShkl = len(G2spc.MustrainNames(SGData))
1509            NDij = len(G2spc.HStrainNames(SGData))
1510            for histoName in newHistList:
1511                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1512                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1513                refList = self.PatternTree.GetItemPyData(
1514                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1515                refList[generalData['Name']] = []
1516        return # success
1517
1518    def OnDummyPowder(self,event):
1519        '''Called in response to Import/Powder Data/Simulate menu item
1520        to create a Dummy powder diffraction data set.
1521
1522        Reads an instrument parameter file and then gets input from the user
1523        '''
1524        # get a list of existing histograms
1525        PWDRlist = []
1526        if self.PatternTree.GetCount():
1527            item, cookie = self.PatternTree.GetFirstChild(self.root)
1528            while item:
1529                name = self.PatternTree.GetItemText(item)
1530                if name.startswith('PWDR ') and name not in PWDRlist:
1531                    PWDRlist.append(name)
1532                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1533        # Initialize a base class reader
1534        rd = G2IO.ImportPowderData(
1535            extensionlist=tuple(),
1536            strictExtension=False,
1537            formatName = 'Simulate dataset',
1538            longFormatName = 'Compute a simulated pattern')
1539        rd.powderentry[0] = '' # no filename
1540        # #self.powderentry[1] = pos # bank offset (N/A here)
1541        rd.powderentry[2] = 1 # only one bank
1542        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1543        self.CheckNotebook()
1544        Iparm = None
1545        lastIparmfile = ''
1546        lastdatafile = ''
1547        self.zipfile = None
1548        # get instrument parameters for it
1549        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1550        if 'T' in Iparm1['Type'][0]:
1551            print('TOF simulation not supported yet')
1552            return False
1553        else:
1554            # need to get name, 2theta start, end, step
1555            rd.idstring = ' CW'
1556            if 'X' in Iparm1['Type'][0]:
1557                rd.idstring = 'CW x-ray simulation'
1558            else:
1559                rd.idstring = 'CW neutron simulation'
1560            # base initial range on wavelength
1561            wave = Iparm1.get('Lam')
1562            if wave:
1563                wave = wave[0]
1564            else:
1565                wave = Iparm1.get('Lam1')
1566                if wave:
1567                    wave = wave[0]
1568        N = 0
1569        while (N < 3): # insist on a dataset with a few points
1570            names = ('dataset name', 'start angle', 'end angle', 'step size')
1571            if not wave or wave < 1.0:
1572                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1573            else:
1574                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1575            dlg = G2G.ScrolledMultiEditor(
1576                self,[inp] * len(inp),range(len(inp)),names,
1577                header='Enter simulation name and range',
1578                minvals=(None,0.001,0.001,0.0001),
1579                maxvals=(None,180.,180.,.1),
1580                sizevals=((225,-1),)
1581                )
1582            dlg.CenterOnParent()
1583            if dlg.ShowModal() == wx.ID_OK:
1584                if inp[1] > inp[2]:
1585                    end,start,step = inp[1:]
1586                else:               
1587                    start,end,step = inp[1:]
1588                step = abs(step)
1589            else:
1590                return False
1591            N = int((end-start)/step)+1
1592            x = np.linspace(start,end,N,True)
1593            N = len(x)
1594        rd.powderdata = [
1595            np.array(x), # x-axis values
1596            np.zeros_like(x), # powder pattern intensities
1597            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1598            np.zeros_like(x), # calc. intensities (zero)
1599            np.zeros_like(x), # calc. background (zero)
1600            np.zeros_like(x), # obs-calc profiles
1601            ]
1602        Tmin = rd.powderdata[0][0]
1603        Tmax = rd.powderdata[0][-1]
1604        # data are read, now store them in the tree
1605        HistName = inp[0]
1606        HistName = 'PWDR '+HistName
1607        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1608        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1609        valuesdict = {
1610            'wtFactor':1.0,
1611            'Dummy':True,
1612            'ranId':ran.randint(0,sys.maxint),
1613            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1614            'qPlot':False,'dPlot':False,'sqrtPlot':False
1615            }
1616        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1617        self.PatternTree.SetItemPyData(
1618            self.PatternTree.AppendItem(Id,text='Comments'),
1619            rd.comments)
1620        self.PatternTree.SetItemPyData(
1621            self.PatternTree.AppendItem(Id,text='Limits'),
1622            [(Tmin,Tmax),[Tmin,Tmax]])
1623        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1624        self.PatternTree.SetItemPyData(
1625            self.PatternTree.AppendItem(Id,text='Background'),
1626            [['chebyschev',True,3,1.0,0.0,0.0],
1627             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1628        self.PatternTree.SetItemPyData(
1629            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1630            [Iparm1,Iparm2])
1631        self.PatternTree.SetItemPyData(
1632            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1633            rd.Sample)
1634        self.PatternTree.SetItemPyData(
1635            self.PatternTree.AppendItem(Id,text='Peak List')
1636            ,{'peaks':[],'sigDict':{}})
1637        self.PatternTree.SetItemPyData(
1638            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1639            [[],[]])
1640        self.PatternTree.SetItemPyData(
1641            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1642            [])
1643        self.PatternTree.SetItemPyData(
1644            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1645            {})
1646        self.PatternTree.Expand(Id)
1647        self.PatternTree.SelectItem(Id)
1648        print('Added simulation powder data '+str(HistName)+ 
1649              ' with parameters from '+str(rd.instmsg))
1650
1651        # make a list of phase names
1652        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1653        phaseNameList = usedHistograms.keys() # phase names in use
1654        if not phaseNameList: return # no phases yet, nothing to do
1655        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1656        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1657        if not result: return
1658        # connect new phases to histograms
1659        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1660        if not sub:
1661            raise Exception('ERROR -- why are there no phases here?')
1662        item, cookie = self.PatternTree.GetFirstChild(sub)
1663        iph = -1
1664        while item: # loop over (new) phases
1665            iph += 1
1666            phaseName = self.PatternTree.GetItemText(item)
1667            data = self.PatternTree.GetItemPyData(item)
1668            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1669            if iph not in result: continue
1670            generalData = data['General']
1671            SGData = generalData['SGData']
1672            UseList = data['Histograms']
1673            NShkl = len(G2spc.MustrainNames(SGData))
1674            NDij = len(G2spc.HStrainNames(SGData))
1675            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
1676            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1677            refList = self.PatternTree.GetItemPyData(
1678                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1679            refList[generalData['Name']] = []
1680        return # success
1681       
1682    def OnPreferences(self,event):
1683        'Edit the GSAS-II configuration variables'
1684        dlg = G2G.SelectConfigSetting(self)
1685        dlg.ShowModal() == wx.ID_OK
1686        dlg.Destroy()
1687
1688    def _Add_ImportMenu_smallangle(self,parent):
1689        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1690        '''
1691        submenu = wx.Menu()
1692        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1693            submenu, help='Import small angle data')
1694        for reader in self.ImportSmallAngleReaderlist:
1695            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1696                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1697            self.ImportMenuId[item.GetId()] = reader
1698            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1699        # item = submenu.Append(wx.ID_ANY,
1700        #     help='Import small angle data, use file to try to determine format',
1701        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1702        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1703
1704    def OnImportSmallAngle(self,event):
1705        '''Called in response to an Import/Small Angle Data/... menu item
1706        to read a small angle diffraction data set.
1707        dict self.ImportMenuId is used to look up the specific
1708        reader item associated with the menu item, which will be
1709        None for the last menu item, which is the "guess" option
1710        where all appropriate formats will be tried.
1711
1712        '''
1713       
1714        def GetSASDIparm(reader):
1715            parm = reader.instdict
1716            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1717                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1718            return Iparm,{}
1719           
1720        # get a list of existing histograms
1721        SASDlist = []
1722        if self.PatternTree.GetCount():
1723            item, cookie = self.PatternTree.GetFirstChild(self.root)
1724            while item:
1725                name = self.PatternTree.GetItemText(item)
1726                if name.startswith('SASD ') and name not in SASDlist:
1727                    SASDlist.append(name)
1728                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1729        # look up which format was requested
1730        reqrdr = self.ImportMenuId.get(event.GetId()) 
1731        rdlist = self.OnImportGeneric(
1732            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1733        if len(rdlist) == 0: return
1734        self.CheckNotebook()
1735        Iparm = None
1736        lastdatafile = ''
1737        newHistList = []
1738        self.EnablePlot = False
1739        for rd in rdlist:
1740            lastdatafile = rd.smallangleentry[0]
1741            HistName = rd.idstring
1742            HistName = 'SASD '+HistName
1743            # make new histogram names unique
1744            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1745            print 'Read small angle data '+str(HistName)+ \
1746                ' from file '+str(self.lastimport)
1747            # data are read, now store them in the tree
1748            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1749            Iparm1,Iparm2 = GetSASDIparm(rd)
1750#            if 'T' in Iparm1['Type'][0]:
1751#                if not rd.clockWd and rd.GSAS:
1752#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1753#                cw = np.diff(rd.powderdata[0])
1754#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1755#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1756#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1757#                if 'Itype' in Iparm2:
1758#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1759#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1760#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1761#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1762#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1763#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1764#                    var += WYI*rd.powderdata[1]**2
1765#                    var /= YI**2
1766#                    rd.powderdata[2] = 1./var
1767#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1768#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1769#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1770            Tmin = min(rd.smallangledata[0])
1771            Tmax = max(rd.smallangledata[0])
1772            valuesdict = {
1773                'wtFactor':1.0,
1774                'Dummy':False,
1775                'ranId':ran.randint(0,sys.maxint),
1776                }
1777            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1778            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1779            self.PatternTree.SetItemPyData(
1780                self.PatternTree.AppendItem(Id,text='Comments'),
1781                rd.comments)
1782            self.PatternTree.SetItemPyData(
1783                self.PatternTree.AppendItem(Id,text='Limits'),
1784                [(Tmin,Tmax),[Tmin,Tmax]])
1785            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1786            self.PatternTree.SetItemPyData(
1787                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1788                [Iparm1,Iparm2])
1789            self.PatternTree.SetItemPyData(
1790                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1791            self.PatternTree.SetItemPyData(
1792                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1793                rd.Sample)
1794            self.PatternTree.SetItemPyData(
1795                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1796            newHistList.append(HistName)
1797        else:
1798            self.EnablePlot = True
1799            self.PatternTree.Expand(Id)
1800            self.PatternTree.SelectItem(Id)
1801           
1802        if not newHistList: return # somehow, no new histograms
1803        return # success
1804
1805    def OnMacroRecordStatus(self,event,setvalue=None):
1806        '''Called when the record macro menu item is used which toggles the
1807        value. Alternately a value to be set can be provided. Note that this
1808        routine is made more complex because on the Mac there are lots of menu
1809        items (listed in self.MacroStatusList) and this loops over all of them.
1810        '''
1811        nextvalue = log.ShowLogStatus() != True
1812        if setvalue is not None:
1813            nextvalue = setvalue
1814        if nextvalue:
1815            log.LogOn()
1816            set2 = True
1817        else:
1818            log.LogOff()
1819            set2 = False
1820        for menuitem in self.MacroStatusList:
1821            menuitem.Check(set2)
1822
1823    def _init_Macro(self):
1824        '''Define the items in the macro menu.
1825        '''
1826        menu = self.MacroMenu
1827        item = menu.Append(
1828                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
1829                kind=wx.ITEM_CHECK,text='Record actions')
1830        self.MacroStatusList.append(item)
1831        item.Check(log.ShowLogStatus())
1832        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
1833
1834        # this may only be of value for development work
1835        item = menu.Append(
1836            help='Show logged commands', id=wx.ID_ANY,
1837            kind=wx.ITEM_NORMAL,text='Show log')
1838        def OnShowLog(event):
1839            print 70*'='
1840            print 'List of logged actions'
1841            for i,line in enumerate(log.G2logList):
1842                if line: print i,line
1843            print 70*'='
1844        self.Bind(wx.EVT_MENU, OnShowLog, item)
1845
1846        item = menu.Append(
1847            help='Clear logged commands', id=wx.ID_ANY,
1848            kind=wx.ITEM_NORMAL,text='Clear log')
1849        def OnClearLog(event): log.G2logList=[None]
1850        self.Bind(wx.EVT_MENU, OnClearLog, item)
1851       
1852        item = menu.Append(
1853            help='Save logged commands to file', id=wx.ID_ANY,
1854            kind=wx.ITEM_NORMAL,text='Save log')
1855        def OnSaveLog(event):
1856            import cPickle
1857            defnam = os.path.splitext(
1858                os.path.split(self.GSASprojectfile)[1]
1859                )[0]+'.gcmd'
1860            dlg = wx.FileDialog(self,
1861                'Choose an file to save past actions', '.', defnam, 
1862                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1863                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1864            dlg.CenterOnParent()
1865            try:
1866                if dlg.ShowModal() == wx.ID_OK:
1867                    filename = dlg.GetPath()
1868                    # make sure extension is correct
1869                    filename = os.path.splitext(filename)[0]+'.gcmd'
1870                else:
1871                    filename = None
1872            finally:
1873                dlg.Destroy()
1874            if filename:
1875                fp = open(filename,'wb')
1876                fp.write(str(len(log.G2logList)-1)+'\n')
1877                for item in log.G2logList:
1878                    if item: cPickle.dump(item,fp)
1879                fp.close()
1880        self.Bind(wx.EVT_MENU, OnSaveLog, item)
1881
1882        item = menu.Append(
1883            help='Load logged commands from file', id=wx.ID_ANY,
1884            kind=wx.ITEM_NORMAL,text='Load log')
1885        def OnLoadLog(event):
1886            # this appends. Perhaps we should ask to clear?
1887            import cPickle
1888            defnam = os.path.splitext(
1889                os.path.split(self.GSASprojectfile)[1]
1890                )[0]+'.gcmd'
1891            dlg = wx.FileDialog(self,
1892                'Choose an file to read saved actions', '.', defnam, 
1893                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1894                wx.OPEN|wx.CHANGE_DIR)
1895            dlg.CenterOnParent()
1896            try:
1897                if dlg.ShowModal() == wx.ID_OK:
1898                    filename = dlg.GetPath()
1899                    # make sure extension is correct
1900                    filename = os.path.splitext(filename)[0]+'.gcmd'
1901                else:
1902                    filename = None
1903            finally:
1904                dlg.Destroy()
1905            if filename and os.path.exists(filename):
1906                fp = open(filename,'rb')
1907                lines = fp.readline()
1908                for i in range(int(lines)):
1909                    log.G2logList.append(cPickle.load(fp))
1910                fp.close()
1911        self.Bind(wx.EVT_MENU, OnLoadLog, item)
1912
1913        item = menu.Append(
1914            help='Replay saved commands', id=wx.ID_ANY,
1915            kind=wx.ITEM_NORMAL,text='Replay log')
1916        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
1917
1918    def _init_Exports(self,menu):
1919        '''Find exporter routines and add them into menus
1920        '''
1921        # set up the top-level menus
1922        projectmenu = wx.Menu()
1923        item = menu.AppendMenu(
1924            wx.ID_ANY, 'Entire project as',
1925            projectmenu, help='Export entire project')
1926
1927        phasemenu = wx.Menu()
1928        item = menu.AppendMenu(
1929            wx.ID_ANY, 'Phase as',
1930            phasemenu, help='Export phase or sometimes phases')
1931
1932        powdermenu = wx.Menu()
1933        item = menu.AppendMenu(
1934            wx.ID_ANY, 'Powder data as',
1935            powdermenu, help='Export powder diffraction histogram(s)')
1936
1937        singlemenu = wx.Menu()
1938        item = menu.AppendMenu(
1939            wx.ID_ANY, 'Single crystal data as',
1940            singlemenu, help='Export single crystal histogram(s)')
1941
1942        imagemenu = wx.Menu()
1943        item = menu.AppendMenu(
1944            wx.ID_ANY, 'Image data as',
1945            imagemenu, help='Export powder image(s) data')
1946
1947        mapmenu = wx.Menu()
1948        item = menu.AppendMenu(
1949            wx.ID_ANY, 'Maps as',
1950            mapmenu, help='Export density map(s)')
1951
1952        # pdfmenu = wx.Menu()
1953        # item = menu.AppendMenu(
1954        #     wx.ID_ANY, 'PDFs as',
1955        #     pdfmenu, help='Export pair distribution function(s)')
1956
1957        # find all the exporter files
1958        pathlist = sys.path
1959        filelist = []
1960        for path in pathlist:
1961            for filename in glob.iglob(os.path.join(path,"G2export*.py")):
1962                filelist.append(filename)   
1963        filelist = sorted(list(set(filelist))) # remove duplicates
1964        self.exporterlist = []
1965        # go through the routines and import them, saving objects that
1966        # have export routines (method Exporter)
1967        for filename in filelist:
1968            path,rootname = os.path.split(filename)
1969            pkg = os.path.splitext(rootname)[0]
1970            try:
1971                fp = None
1972                fp, fppath,desc = imp.find_module(pkg,[path,])
1973                pkg = imp.load_module(pkg,fp,fppath,desc)
1974                for clss in inspect.getmembers(pkg): # find classes defined in package
1975                    if clss[0].startswith('_'): continue
1976                    if inspect.isclass(clss[1]):
1977                        # check if we have the required methods
1978                        for m in 'Exporter','loadParmDict':
1979                            if not hasattr(clss[1],m): break
1980                            if not callable(getattr(clss[1],m)): break
1981                        else:
1982                            exporter = clss[1](self) # create an export instance
1983                            self.exporterlist.append(exporter)
1984            except AttributeError:
1985                print 'Import_'+errprefix+': Attribute Error'+str(filename)
1986                pass
1987            except ImportError:
1988                print 'Import_'+errprefix+': Error importing file'+str(filename)
1989                pass
1990            if fp: fp.close()
1991        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
1992        for obj in self.exporterlist:
1993            #print 'exporter',obj
1994            for typ in obj.exporttype:
1995                if typ == "project":
1996                    submenu = projectmenu
1997                elif typ == "phase":
1998                    submenu = phasemenu
1999                elif typ == "powder":
2000                    submenu = powdermenu
2001                elif typ == "single":
2002                    submenu = singlemenu
2003                elif typ == "image":
2004                    submenu = imagemenu
2005                elif typ == "map":
2006                    submenu = mapmenu
2007                # elif typ == "pdf":
2008                #     submenu = pdfmenu
2009                else:
2010                    print("Error, unknown type in "+str(obj))
2011                    break
2012                item = submenu.Append(
2013                    wx.ID_ANY,
2014                    help=obj.longFormatName,
2015                    kind=wx.ITEM_NORMAL,
2016                    text=obj.formatName)
2017                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
2018                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
2019               
2020        #code to debug an Exporter. hard-coded the routine below, to allow a reload before use
2021        # def DebugExport(event):
2022        #      print 'start reload'
2023        #      reload(G2IO)
2024        #      import G2export_pwdr as dev
2025        #      reload(dev)
2026        #      dev.ExportPowderFXYE(self).Exporter(event)
2027        # item = menu.Append(
2028        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2029        #     help="debug exporter",text="test Export FXYE")
2030        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
2031        # # #self.ExportLookup[item.GetId()] = 'image'
2032        # self.ExportLookup[item.GetId()] = 'powder'
2033
2034    def _Add_ExportMenuItems(self,parent):
2035        # item = parent.Append(
2036        #     help='Select PWDR item to enable',id=wx.ID_ANY,
2037        #     kind=wx.ITEM_NORMAL,
2038        #     text='Export Powder Patterns...')
2039        # self.ExportPattern.append(item)
2040        # item.Enable(False)
2041        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
2042
2043        item = parent.Append(
2044            help='',id=wx.ID_ANY,
2045            kind=wx.ITEM_NORMAL,
2046            text='Export All Peak Lists...')
2047        self.ExportPeakList.append(item)
2048        item.Enable(True)
2049        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
2050
2051        item = parent.Append(
2052            help='',id=wx.ID_ANY,
2053            kind=wx.ITEM_NORMAL,
2054            text='Export HKLs...')
2055        self.ExportHKL.append(item)
2056        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
2057
2058        item = parent.Append(
2059            help='Select PDF item to enable',
2060            id=wx.ID_ANY,
2061            kind=wx.ITEM_NORMAL,
2062            text='Export PDF...')
2063        self.ExportPDF.append(item)
2064        item.Enable(False)
2065        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
2066
2067    def FillMainMenu(self,menubar):
2068        '''Define contents of the main GSAS-II menu for the (main) data tree window.
2069        For the mac, this is also called for the data item windows as well so that
2070        the main menu items are data menu as well.
2071        '''
2072        File = wx.Menu(title='')
2073        menubar.Append(menu=File, title='&File')
2074        self._Add_FileMenuItems(File)
2075        Data = wx.Menu(title='')
2076        menubar.Append(menu=Data, title='Data')
2077        self._Add_DataMenuItems(Data)
2078        Calculate = wx.Menu(title='')       
2079        menubar.Append(menu=Calculate, title='&Calculate')
2080        self._Add_CalculateMenuItems(Calculate)
2081        Import = wx.Menu(title='')       
2082        menubar.Append(menu=Import, title='Import')
2083        self._Add_ImportMenu_Image(Import)
2084        self._Add_ImportMenu_Phase(Import)
2085        self._Add_ImportMenu_powder(Import)
2086        self._Add_ImportMenu_Sfact(Import)
2087        self._Add_ImportMenu_smallangle(Import)
2088        item = File.Append(wx.ID_PREFERENCES, text = "&Preferences")
2089        self.Bind(wx.EVT_MENU, self.OnPreferences, item)
2090
2091        #======================================================================
2092        # Code to help develop/debug an importer, much is hard-coded below
2093        # but module is reloaded before each use, allowing faster testing
2094        # def DebugImport(event):
2095        #     print 'start reload'
2096        #     import G2phase_ISO as dev
2097        #     reload(dev)
2098        #     rd = dev.ISODISTORTPhaseReader()
2099        #     self.ImportMenuId[event.GetId()] = rd
2100        #     self.OnImportPhase(event)
2101            # or ----------------------------------------------------------------------
2102            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
2103            # special debug code
2104            # or ----------------------------------------------------------------------
2105            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
2106            # fp = open(filename,'Ur')
2107            # if not rd.ContentsValidator(fp):
2108            #     print 'not validated'
2109            #     # make a list of used phase ranId's
2110            # phaseRIdList = []
2111            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2112            # if sub:
2113            #     item, cookie = self.PatternTree.GetFirstChild(sub)
2114            #     while item:
2115            #         phaseName = self.PatternTree.GetItemText(item)
2116            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2117            #         if ranId: phaseRIdList.append(ranId)
2118            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2119            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2120            #     print 'read OK'
2121        # item = Import.Append(
2122        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2123        #     help="debug importer",text="test importer")
2124        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2125        #======================================================================
2126        self.ExportMenu = wx.Menu(title='')
2127        menubar.Append(menu=self.ExportMenu, title='Export')
2128        self._init_Exports(self.ExportMenu)
2129        self._Add_ExportMenuItems(self.ExportMenu)
2130        if GSASIIpath.GetConfigValue('Enable_logging'):
2131            self.MacroMenu = wx.Menu(title='')
2132            menubar.Append(menu=self.MacroMenu, title='Macro')
2133            self._init_Macro()
2134        HelpMenu=G2G.MyHelp(self,helpType='Data tree',
2135            morehelpitems=[
2136                           ('&Tutorials','Tutorials'), 
2137                           ])
2138        menubar.Append(menu=HelpMenu,title='&Help')
2139
2140    def _init_ctrls(self, parent):
2141        wx.Frame.__init__(self, name='GSASII', parent=parent,
2142            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2143        clientSize = wx.ClientDisplayRect()
2144        Size = self.GetSize()
2145        xPos = clientSize[2]-Size[0]
2146        self.SetPosition(wx.Point(xPos,clientSize[1]))
2147        self._init_Imports()
2148        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2149        self.MakePDF = []
2150        self.Refine = []
2151        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2152        #self.ExportPattern = []
2153        self.ExportPeakList = []
2154        self.ExportHKL = []
2155        self.ExportPDF = []
2156        self.ExportPhase = []
2157        self.ExportCIF = []
2158        #
2159        self.GSASIIMenu = wx.MenuBar()
2160        # create a list of all dataframe menus (appended in PrefillDataMenu)
2161        self.dataMenuBars = [self.GSASIIMenu]
2162        self.MacroStatusList = []
2163        self.FillMainMenu(self.GSASIIMenu)
2164        self.SetMenuBar(self.GSASIIMenu)
2165        self.Bind(wx.EVT_SIZE, self.OnSize)
2166        self.Status = self.CreateStatusBar()
2167        self.mainPanel = wx.Panel(self,-1)
2168       
2169        wxID_PATTERNTREE = wx.NewId()
2170        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2171        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2172            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2173        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnPatternTreeSelChanged)
2174        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2175            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2176        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2177            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2178        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2179            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2180        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2181            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2182        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2183            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2184        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2185            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2186        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2187        self.root = self.PatternTree.root
2188        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2189            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2190        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
2191        plotFrame.Show()
2192       
2193        self.dataDisplay = None
2194       
2195    def __init__(self, parent):
2196        self.ExportLookup = {}
2197        self._init_ctrls(parent)
2198        self.Image = wx.Image(
2199            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2200            wx.BITMAP_TYPE_ICO)
2201        if "wxMSW" in wx.PlatformInfo:
2202            img = self.Image.Scale(16, 16).ConvertToBitmap()
2203        elif "wxGTK" in wx.PlatformInfo:
2204            img = self.Image.Scale(22, 22).ConvertToBitmap()
2205        else:
2206            img = self.Image.ConvertToBitmap()
2207        self.SetIcon(wx.IconFromBitmap(img))
2208        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2209        # various defaults
2210        self.oldFocus = None
2211        self.GSASprojectfile = ''
2212        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
2213        self.exportDir = None  # the last directory used for exports, if any.
2214        self.undofile = ''
2215        self.TreeItemDelete = False
2216        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2217        self.Weight = False
2218        self.IfPlot = False
2219        self.DDShowAll = False
2220        self.PatternId = 0
2221        self.PickId = 0
2222        self.PickIdText = None
2223        self.PeakTable = []
2224        self.LimitsTable = []
2225        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2226        self.HKL = []
2227        self.Lines = []
2228        self.itemPicked = None
2229        self.dataFrame = None
2230        self.Interpolate = 'nearest'
2231        self.ContourColor = 'Paired'
2232        self.VcovColor = 'RdYlGn'
2233        self.RamaColor = 'Blues'
2234        self.Projection = 'equal area'
2235        self.logPlot = False
2236        self.sqPlot = False
2237        self.ErrorBars = False
2238        self.Contour = False
2239        self.Legend = False
2240        self.SinglePlot = True
2241        self.SubBack = False
2242        self.seqReverse = False
2243        self.seqLines = True #draw lines between points
2244        self.plotView = 0
2245        self.Image = 0
2246        self.oldImagefile = '' # the name of the last image file read
2247        self.ImageZ = []  # this contains the image plotted and used for integration
2248        # self.ImageZ and self.oldImagefile are set in GSASIIplot.PlotImage
2249        # and GSASIIIO.ReadImageData (GetImageData soon)
2250        # any changes to self.ImageZ should initialize self.oldImagefile to force a reread
2251        self.Integrate = 0
2252        self.imageDefault = {}
2253        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2254        self.autoIntFrame = None
2255        self.IntegratedList = [] # list of integrated image files
2256        self.Sngl = False
2257        self.ifGetRing = False
2258        self.MaskKey = ''           #trigger for making image masks
2259        self.StrainKey = ''         #ditto for new strain d-zeros
2260        self.EnablePlot = True
2261        self.hist = ''              # selected histogram in Phase/Data tab
2262        if GSASIIpath.GetConfigValue('Starting_directory'):
2263            try: 
2264                os.chdir(GSASIIpath.GetConfigValue('Starting_directory'))
2265            except:
2266                print('Ignoring Config Starting_directory value: '+
2267                      GSASIIpath.GetConfigValue('Starting_directory'))
2268        arg = sys.argv
2269        if len(arg) > 1 and arg[1]:
2270            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2271            self.dirname = os.path.dirname(arg[1])
2272            if self.dirname: os.chdir(self.dirname)
2273            try:
2274                self.StartProject()         #open the file if possible
2275            except Exception:
2276                print 'Error opening or reading file',arg[1]
2277                import traceback
2278                print traceback.format_exc()
2279             
2280        self.ImportDir = os.path.normpath(os.getcwd()) # specifies a default path to be used for imports
2281        if GSASIIpath.GetConfigValue('Import_directory'):
2282            self.ImportDir = GSASIIpath.GetConfigValue('Import_directory')
2283           
2284    def GetTreeItemsList(self,item):
2285        return self.PatternTree._getTreeItemsList(item)
2286
2287    def OnSize(self,event):
2288        'Called when the main window is resized. Not sure why'
2289        w,h = self.GetClientSizeTuple()
2290        self.mainPanel.SetSize(wx.Size(w,h))
2291        self.PatternTree.SetSize(wx.Size(w,h))
2292                       
2293    def OnPatternTreeSelChanged(self, event):
2294        '''Called when a data tree item is selected'''
2295        if self.TreeItemDelete:
2296            self.TreeItemDelete = False
2297        else:
2298            pltNum = self.G2plotNB.nb.GetSelection()
2299            if pltNum >= 0:                         #to avoid the startup with no plot!
2300                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2301                pltPlot = pltPage.figure
2302            item = event.GetItem()
2303            G2gd.MovePatternTreeToGrid(self,item)
2304            if self.oldFocus:
2305                self.oldFocus.SetFocus()
2306       
2307    def OnPatternTreeItemCollapsed(self, event):
2308        'Called when a tree item is collapsed - all children will be collapsed'
2309        self.PatternTree.CollapseAllChildren(event.GetItem())
2310
2311    def OnPatternTreeItemExpanded(self, event):
2312        'Called when a tree item is expanded'
2313        self.OnPatternTreeSelChanged(event)
2314        event.Skip()
2315       
2316    def OnPatternTreeItemDelete(self, event):
2317        'Called when a tree item is deleted -- not sure what this does'
2318        self.TreeItemDelete = True
2319
2320    def OnPatternTreeItemActivated(self, event):
2321        'Called when a tree item is activated'
2322        event.Skip()
2323       
2324    def OnPatternTreeBeginRDrag(self,event):
2325        event.Allow()
2326        self.BeginDragId = event.GetItem()
2327        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2328        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2329        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2330        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2331        while item:     #G2 data tree has no sub children under a child of a tree item
2332            name = self.PatternTree.GetItemText(item)
2333            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2334            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2335       
2336    def OnPatternTreeEndDrag(self,event):
2337        event.Allow()
2338        self.EndDragId = event.GetItem()
2339        try:
2340            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2341        except:
2342            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2343            NewParent = self.root
2344        if self.ParentId != NewParent:
2345            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2346        else:
2347            Name,Item = self.DragData[0]
2348            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2349            self.PatternTree.SetItemPyData(NewId,Item)
2350            for name,item in self.DragData[1:]:     #loop over children
2351                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2352                self.PatternTree.SetItemPyData(Id,item)
2353            self.PatternTree.Delete(self.BeginDragId)
2354            G2gd.MovePatternTreeToGrid(self,NewId)
2355       
2356    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2357        'Allows stepping through the tree with the up/down arrow keys'
2358        self.oldFocus = wx.Window.FindFocus()
2359        keyevt = event.GetKeyEvent()
2360        key = event.GetKeyCode()
2361        item = self.PatternTree.GetSelection()
2362        if type(item) is int: return # is this the toplevel in tree?
2363        name = self.PatternTree.GetItemText(item)
2364        parent = self.PatternTree.GetItemParent(item)
2365        if key == wx.WXK_UP:
2366            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2367                if type(parent) is int: return # is this the toplevel in tree?
2368                prev = self.PatternTree.GetPrevSibling(parent)
2369                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2370                if NewId:
2371                    self.PatternTree.Collapse(parent)
2372                    self.PatternTree.Expand(prev)
2373                    self.oldFocus = wx.Window.FindFocus()
2374                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2375                else:
2376                    wx.CallAfter(self.PatternTree.SelectItem,item)
2377            else:   
2378                self.PatternTree.GetPrevSibling(item)
2379                self.PatternTree.SelectItem(item)
2380        elif key == wx.WXK_DOWN:
2381            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2382                next = self.PatternTree.GetNextSibling(parent)
2383                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2384                if NewId:
2385                    self.PatternTree.Collapse(parent)
2386                    self.PatternTree.Expand(next)
2387                    self.oldFocus = wx.Window.FindFocus()
2388                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2389                else:
2390                    wx.CallAfter(self.PatternTree.SelectItem,item)
2391            else:   
2392                self.PatternTree.GetNextSibling(item)
2393                self.PatternTree.SelectItem(item)
2394               
2395    def OnReadPowderPeaks(self,event):
2396        'Bound to menu Data/Read Powder Peaks'
2397        Cuka = 1.54052
2398        self.CheckNotebook()
2399        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
2400            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
2401        try:
2402            if dlg.ShowModal() == wx.ID_OK:
2403                self.HKL = []
2404                self.powderfile = dlg.GetPath()
2405                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
2406                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2407                data = ['PKS',Cuka,0.0]
2408                names = ['Type','Lam','Zero'] 
2409                codes = [0,0,0]
2410                inst = [G2IO.makeInstDict(names,data,codes),{}]
2411                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2412                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2413                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2414                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2415                self.PatternTree.Expand(Id)
2416                self.PatternTree.SelectItem(Id)
2417                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2418        finally:
2419            dlg.Destroy()
2420                       
2421    def OnImageRead(self,event):
2422        'Called to read in an image in any known format'
2423        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2424        self.CheckNotebook()
2425        dlg = wx.FileDialog(
2426            self, 'Choose image files', '.', '',
2427            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png)|'
2428            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png;*.zip|'
2429            'European detector file (*.edf)|*.edf|'
2430            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2431            'MAR file (*.mar*)|*.mar*|'
2432            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2433            'ADSC Image (*.img)|*.img|'
2434            'Rigaku R-Axis IV (*.stl)|*.stl|'
2435            'GSAS-II Image (*.G2img)|*.G2img|'
2436            'Portable Network Graphics image (*.png)|*.png|'
2437            'Zip archive (*.zip)|*.zip|'
2438            'All files (*.*)|*.*',
2439            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
2440        try:
2441            if dlg.ShowModal() == wx.ID_OK:
2442                imagefiles = dlg.GetPaths()
2443                imagefiles.sort()
2444                for imagefile in imagefiles:
2445                    G2IO.ReadLoadImage(imagefile,self)
2446                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2447                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,self.Image,'Image Controls'))             #show last image to be read
2448        finally:
2449            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2450            os.chdir(path)
2451            dlg.Destroy()
2452
2453    def CheckNotebook(self):
2454        '''Make sure the data tree has the minimally expected controls.
2455        '''
2456        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2457            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2458            self.PatternTree.SetItemPyData(sub,[''])
2459        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2460            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2461            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2462        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2463            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2464            self.PatternTree.SetItemPyData(sub,{})
2465        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2466            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2467            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2468        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2469            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2470            self.PatternTree.SetItemPyData(sub,{})
2471        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2472            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2473            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2474                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2475               
2476    class CopyDialog(wx.Dialog):
2477        '''Creates a dialog for copying control settings between
2478        data tree items'''
2479        def __init__(self,parent,title,text,data):
2480            wx.Dialog.__init__(self,parent,-1,title, 
2481                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2482            self.data = data
2483            panel = wx.Panel(self)
2484            mainSizer = wx.BoxSizer(wx.VERTICAL)
2485            topLabl = wx.StaticText(panel,-1,text)
2486            mainSizer.Add((10,10),1)
2487            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2488            mainSizer.Add((10,10),1)
2489            ncols = len(data)/40+1
2490            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2491            for id,item in enumerate(self.data):
2492                ckbox = wx.CheckBox(panel,id,item[1])
2493                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2494                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2495            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2496            OkBtn = wx.Button(panel,-1,"Ok")
2497            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2498            cancelBtn = wx.Button(panel,-1,"Cancel")
2499            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2500            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2501            btnSizer.Add((20,20),1)
2502            btnSizer.Add(OkBtn)
2503            btnSizer.Add((20,20),1)
2504            btnSizer.Add(cancelBtn)
2505            btnSizer.Add((20,20),1)
2506           
2507            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2508            panel.SetSizer(mainSizer)
2509            panel.Fit()
2510            self.Fit()
2511       
2512        def OnCopyChange(self,event):
2513            id = event.GetId()
2514            self.data[id][0] = self.FindWindowById(id).GetValue()       
2515           
2516        def OnOk(self,event):
2517            parent = self.GetParent()
2518            parent.Raise()
2519            self.EndModal(wx.ID_OK)             
2520           
2521        def OnCancel(self,event):
2522            parent = self.GetParent()
2523            parent.Raise()
2524            self.EndModal(wx.ID_CANCEL)             
2525           
2526        def GetData(self):
2527            return self.data
2528       
2529    class SumDialog(wx.Dialog):
2530        'Allows user to supply scale factor(s) when summing data'
2531        def __init__(self,parent,title,text,dataType,data):
2532            wx.Dialog.__init__(self,parent,-1,title, 
2533                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2534            self.data = data
2535            panel = wx.Panel(self)
2536            mainSizer = wx.BoxSizer(wx.VERTICAL)
2537            topLabl = wx.StaticText(panel,-1,text)
2538            mainSizer.Add((10,10),1)
2539            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2540            mainSizer.Add((10,10),1)
2541            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2542            for id,item in enumerate(self.data[:-1]):
2543                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2544                name.SetEditable(False)
2545                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2546                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2547                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2548                dataGridSizer.Add(scale,0,wx.LEFT,10)
2549                dataGridSizer.Add(name,0,wx.RIGHT,10)
2550            if dataType:
2551                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2552                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2553                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2554                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2555                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2556                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2557            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2558            OkBtn = wx.Button(panel,-1,"Ok")
2559            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2560            cancelBtn = wx.Button(panel,-1,"Cancel")
2561            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2562            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2563            btnSizer.Add((20,20),1)
2564            btnSizer.Add(OkBtn)
2565            btnSizer.Add((20,20),1)
2566            btnSizer.Add(cancelBtn)
2567            btnSizer.Add((20,20),1)
2568           
2569            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2570            panel.SetSizer(mainSizer)
2571            panel.Fit()
2572            self.Fit()
2573
2574        def OnScaleChange(self,event):
2575            id = event.GetId()
2576            value = self.FindWindowById(id).GetValue()
2577            try:
2578                self.data[id][0] = float(value)
2579                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2580            except ValueError:
2581                if value and '-' not in value[0]:
2582                    print 'bad input - numbers only'
2583                    self.FindWindowById(id).SetValue('0.000')
2584           
2585        def OnNameChange(self,event):
2586            self.data[-1] = self.name.GetValue() 
2587           
2588        def OnOk(self,event):
2589            parent = self.GetParent()
2590            parent.Raise()
2591            self.EndModal(wx.ID_OK)             
2592           
2593        def OnCancel(self,event):
2594            parent = self.GetParent()
2595            parent.Raise()
2596            self.EndModal(wx.ID_CANCEL)             
2597           
2598        def GetData(self):
2599            return self.data
2600                       
2601    def OnPwdrSum(self,event):
2602        'Sum together powder data(?)'
2603        TextList = []
2604        DataList = []
2605        SumList = []
2606        Names = []
2607        Inst = None
2608        SumItemList = []
2609        Comments = ['Sum equals: \n']
2610        if self.PatternTree.GetCount():
2611            item, cookie = self.PatternTree.GetFirstChild(self.root)
2612            while item:
2613                name = self.PatternTree.GetItemText(item)
2614                Names.append(name)
2615                if 'PWDR' in name:
2616                    TextList.append([0.0,name])
2617                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2618                    if not Inst:
2619                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2620                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2621            if len(TextList) < 2:
2622                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2623                return
2624            TextList.append('default_sum_name')               
2625            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2626            try:
2627                if dlg.ShowModal() == wx.ID_OK:
2628                    lenX = 0
2629                    Xminmax = [0,0]
2630                    Xsum = []
2631                    Ysum = []
2632                    Vsum = []
2633                    result = dlg.GetData()
2634                    for i,item in enumerate(result[:-1]):
2635                        scale,name = item
2636                        data = DataList[i]
2637                        if scale:
2638                            Comments.append("%10.3f %s" % (scale,' * '+name))
2639                            x,y,w,yc,yb,yd = data   #numpy arrays!
2640                            v = 1./w
2641                            if lenX:
2642                                if lenX != len(x):
2643                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2644                                        '\nExpected:'+str(lenX)+ \
2645                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2646                                    return
2647                            else:
2648                                lenX = len(x)
2649                            if Xminmax[1]:
2650                                if Xminmax != [x[0],x[-1]]:
2651                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2652                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2653                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2654                                    return
2655                                else:
2656                                    for j,yi in enumerate(y):
2657                                         Ysum[j] += scale*yi
2658                                         Vsum[j] += abs(scale)*v[j]
2659                            else:
2660                                Xminmax = [x[0],x[-1]]
2661                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2662                                for j,yi in enumerate(y):
2663                                    Xsum.append(x[j])
2664                                    Ysum.append(scale*yi)
2665                                    Vsum.append(abs(scale*v[j]))
2666                    Wsum = 1./np.array(Vsum)
2667                    outname = 'PWDR '+result[-1]
2668                    Id = 0
2669                    if outname in Names:
2670                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2671                        try:
2672                            if dlg2.ShowModal() == wx.ID_OK:
2673                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2674                                self.PatternTree.Delete(Id)
2675                        finally:
2676                            dlg2.Destroy()
2677                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2678                    if Id:
2679                        Sample = G2pdG.SetDefaultSample()
2680                        valuesdict = {
2681                            'wtFactor':1.0,
2682                            'Dummy':False,
2683                            'ranId':ran.randint(0,sys.maxint),
2684                            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
2685                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2686                            }
2687                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2688                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2689                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2690                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2691                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2692                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2693                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2694                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2695                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2696                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2697                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2698                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2699                        self.PatternTree.SelectItem(Id)
2700                        self.PatternTree.Expand(Id)
2701            finally:
2702                dlg.Destroy()
2703
2704    def OnImageSum(self,event):
2705        'Sum together image data(?)'
2706        TextList = []
2707        DataList = []
2708        SumList = []
2709        Names = []
2710        Inst = []
2711        SumItemList = []
2712        Comments = ['Sum equals: \n']
2713        if self.PatternTree.GetCount():
2714            item, cookie = self.PatternTree.GetFirstChild(self.root)
2715            while item:
2716                name = self.PatternTree.GetItemText(item)
2717                Names.append(name)
2718                if 'IMG' in name:
2719                    TextList.append([0.0,name])
2720                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2721                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2722                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2723            if len(TextList) < 2:
2724                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2725                return
2726            TextList.append('default_sum_name')               
2727            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2728            try:
2729                if dlg.ShowModal() == wx.ID_OK:
2730                    imSize = 0
2731                    result = dlg.GetData()
2732                    First = True
2733                    Found = False
2734                    for i,item in enumerate(result[:-1]):
2735                        scale,name = item
2736                        data = DataList[i]
2737                        if scale:
2738                            Found = True                               
2739                            Comments.append("%10.3f %s" % (scale,' * '+name))
2740                            Npix,imagefile = data
2741                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2742                            if First:
2743                                newImage = np.zeros_like(image)
2744                                First = False
2745                            if imSize:
2746                                if imSize != Npix:
2747                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2748                                        '\nExpected:'+str(imSize)+ \
2749                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2750                                    return
2751                                newImage = newImage+scale*image
2752                            else:
2753                                imSize = Npix
2754                                newImage = newImage+scale*image
2755                            del(image)
2756                    if not Found:
2757                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2758                        return
2759                       
2760                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2761                    outname = 'IMG '+result[-1]
2762                    Id = 0
2763                    if outname in Names:
2764                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2765                        try:
2766                            if dlg2.ShowModal() == wx.ID_OK:
2767                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2768                        finally:
2769                            dlg2.Destroy()
2770                    else:
2771                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2772                    if Id:
2773                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2774                            'G2img files (*.G2img)|*.G2img', 
2775                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2776                        if dlg.ShowModal() == wx.ID_OK:
2777                            newimagefile = dlg.GetPath()
2778                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2779                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2780                            Imax = np.amax(newImage)
2781                            Imin = np.amin(newImage)
2782                            newImage = []
2783                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2784                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2785                        del(newImage)
2786                        if self.imageDefault:
2787                            Data = copy.copy(self.imageDefault)
2788                        Data['showLines'] = True
2789                        Data['ring'] = []
2790                        Data['rings'] = []
2791                        Data['cutoff'] = 10
2792                        Data['pixLimit'] = 20
2793                        Data['ellipses'] = []
2794                        Data['calibrant'] = ''
2795                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2796                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2797                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2798                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2799                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2800                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2801                        self.PatternTree.SelectItem(Id)
2802                        self.PatternTree.Expand(Id)
2803                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2804                        self.Image = self.PickId
2805            finally:
2806                dlg.Destroy()
2807                     
2808    def OnAddPhase(self,event):
2809        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2810        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2811            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2812        else:
2813            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2814        PhaseName = ''
2815        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2816            style=wx.OK)
2817        if dlg.ShowModal() == wx.ID_OK:
2818            PhaseName = dlg.GetValue()
2819        dlg.Destroy()
2820        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2821        E,SGData = G2spc.SpcGroup('P 1')
2822        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2823       
2824    def OnDeletePhase(self,event):
2825        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2826        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2827        if self.dataFrame:
2828            self.dataFrame.Clear() 
2829        TextList = []
2830        DelList = []
2831        DelItemList = []
2832        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2833            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2834        else:
2835            return
2836        if sub:
2837            item, cookie = self.PatternTree.GetFirstChild(sub)
2838            while item:
2839                TextList.append(self.PatternTree.GetItemText(item))
2840                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2841            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2842            try:
2843                if dlg.ShowModal() == wx.ID_OK:
2844                    result = dlg.GetSelections()
2845                    for i in result: DelList.append([i,TextList[i]])
2846                    item, cookie = self.PatternTree.GetFirstChild(sub)
2847                    i = 0
2848                    while item:
2849                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2850                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2851                        i += 1
2852                    for item in DelItemList:
2853                        name = self.PatternTree.GetItemText(item)
2854                        self.PatternTree.Delete(item)
2855                        self.G2plotNB.Delete(name)
2856                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2857                    while item:
2858                        name = self.PatternTree.GetItemText(item)
2859                        if 'PWDR' in name:
2860                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2861                            refList = self.PatternTree.GetItemPyData(Id)
2862                            if len(refList):
2863                                for i,item in DelList:
2864                                    if item in refList:
2865                                        del(refList[item])
2866                            self.PatternTree.SetItemPyData(Id,refList)
2867                        elif 'HKLF' in name:
2868                            data = self.PatternTree.GetItemPyData(item)
2869                            data[0] = {}
2870                            self.PatternTree.SetItemPyData(item,data)
2871                           
2872                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2873            finally:
2874                dlg.Destroy()
2875               
2876    def OnRenameData(self,event):
2877        'Renames an existing phase. Called by Data/Rename Phase menu'
2878        name = self.PatternTree.GetItemText(self.PickId)     
2879        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2880            dataType = name[:name.index(' ')+1]                 #includes the ' '
2881            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2882                defaultValue=name[name.index(' ')+1:])
2883            try:
2884                if dlg.ShowModal() == wx.ID_OK:
2885                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2886            finally:
2887                dlg.Destroy()
2888       
2889    def GetFileList(self,fileType,skip=None):        #potentially useful?
2890        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2891        fileList = []
2892        Source = ''
2893        id, cookie = self.PatternTree.GetFirstChild(self.root)
2894        while id:
2895            name = self.PatternTree.GetItemText(id)
2896            if fileType in name:
2897                if id == skip:
2898                    Source = name
2899                else:
2900                    fileList.append([False,name,id])
2901            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2902        if skip:
2903            return fileList,Source
2904        else:
2905            return fileList
2906           
2907    def OnDataDelete(self, event):
2908        '''Delete one or more histograms from data tree. Called by the
2909        Data/DeleteData menu
2910        '''
2911        TextList = []
2912        DelList = []
2913        DelItemList = []
2914        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
2915        ifPWDR = False
2916        ifSASD = False
2917        ifIMG = False
2918        ifHKLF = False
2919        ifPDF = False
2920        if self.PatternTree.GetCount():
2921            item, cookie = self.PatternTree.GetFirstChild(self.root)
2922            while item:
2923                name = self.PatternTree.GetItemText(item)
2924                if name not in ['Notebook','Controls','Covariance','Constraints',
2925                    'Restraints','Phases','Rigid bodies','Sequential results']:
2926                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
2927                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
2928                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
2929                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
2930                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
2931                    TextList.append(name)
2932                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2933            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2934            try:
2935                if dlg.ShowModal() == wx.ID_OK:
2936                    result = dlg.GetSelections()
2937                    for i in result: DelList.append(TextList[i])
2938                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2939                    while item:
2940                        itemName = self.PatternTree.GetItemText(item)
2941                        if itemName in DelList:
2942                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
2943                            elif 'SASD' in itemName: nItems['SASD'] -= 1
2944                            elif 'IMG' in itemName: nItems['IMG'] -= 1
2945                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
2946                            elif 'PDF' in itemName: nItems['PDF'] -= 1
2947                            DelItemList.append(item)
2948                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2949                    for item in DelItemList:
2950                        self.PatternTree.Delete(item)
2951                    self.PickId = 0
2952                    self.PickIdText = None
2953                    self.PatternId = 0
2954                    if nItems['PWDR']:
2955                        wx.CallAfter(G2plt.PlotPatterns,self,True)
2956                    else:
2957                        self.G2plotNB.Delete('Powder Patterns')
2958                    if not nItems['IMG']:
2959                        self.G2plotNB.Delete('2D Powder Image')
2960                    if not nItems['HKLF']:
2961                        self.G2plotNB.Delete('Structure Factors')
2962                        if '3D Structure Factors' in self.G2plotNB.plotList:
2963                            self.G2plotNB.Delete('3D Structure Factors')
2964            finally:
2965                dlg.Destroy()
2966
2967    def OnFileOpen(self, event, filename=None):
2968        '''Gets a GSAS-II .gpx project file in response to the
2969        File/Open Project menu button
2970        '''
2971        result = wx.ID_OK
2972        self.EnablePlot = False
2973        if self.PatternTree.GetChildrenCount(self.root,False):
2974            if self.dataFrame:
2975                self.dataFrame.Clear() 
2976            dlg = wx.MessageDialog(
2977                self,
2978                'Do you want to overwrite the current project? '
2979                'Any unsaved changes will be lost. Press OK to continue.',
2980                'Overwrite?',  wx.OK | wx.CANCEL)
2981            try:
2982                result = dlg.ShowModal()
2983                if result == wx.ID_OK:
2984                    self.PatternTree.DeleteChildren(self.root)
2985                    self.GSASprojectfile = ''
2986                    self.HKL = []
2987                    if self.G2plotNB.plotList:
2988                        self.G2plotNB.clear()
2989            finally:
2990                dlg.Destroy()
2991        if result != wx.ID_OK: return
2992
2993        if not filename:
2994            if self.dataDisplay: self.dataDisplay.Destroy()
2995            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2996                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2997            try:
2998                if dlg.ShowModal() != wx.ID_OK: return
2999                self.GSASprojectfile = dlg.GetPath()
3000                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3001                self.dirname = dlg.GetDirectory()
3002            finally:
3003                dlg.Destroy()
3004        else:
3005            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3006            self.dirname = os.path.split(filename)[0]
3007
3008        try:
3009            self.StartProject()         #open the file if possible
3010        except:
3011            print '\nError opening file ',filename
3012            import traceback
3013            print traceback.format_exc()
3014       
3015    def StartProject(self):
3016        '''Opens a GSAS-II project file & selects the 1st available data set to
3017        display (PWDR, HKLF or SASD)
3018        '''
3019       
3020        Id = 0
3021        G2IO.ProjFileOpen(self)
3022        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3023        self.PatternTree.Expand(self.root)
3024        self.HKL = []
3025        item, cookie = self.PatternTree.GetFirstChild(self.root)
3026        while item and not Id:
3027            name = self.PatternTree.GetItemText(item)
3028            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3029                Id = item
3030            elif name == 'Controls':
3031                data = self.PatternTree.GetItemPyData(item)
3032                if data:
3033                    for item in self.Refine: item.Enable(True)
3034                    self.EnableSeqRefineMenu()
3035            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3036        if Id:
3037            self.EnablePlot = True
3038            self.PatternTree.SelectItem(Id)
3039        self.CheckNotebook()
3040        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3041
3042    def OnFileClose(self, event):
3043        '''Clears the data tree in response to the
3044        File/New Project menu button. User is given option to save
3045        the project.
3046        '''
3047        if self.dataFrame:
3048            self.dataFrame.Clear()
3049            self.dataFrame.SetLabel('GSAS-II data display') 
3050        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3051        try:
3052            result = dlg.ShowModal()
3053            if result == wx.ID_OK:
3054                self.OnFileSaveMenu(event)
3055            if result != wx.ID_CANCEL:
3056                self.GSASprojectfile = ''
3057                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3058                self.PatternTree.DeleteChildren(self.root)
3059                if self.HKL: self.HKL = []
3060                if self.G2plotNB.plotList:
3061                    self.G2plotNB.clear()
3062        finally:
3063            dlg.Destroy()
3064
3065    def OnFileSave(self, event):
3066        '''Save the current project in response to the
3067        File/Save Project menu button
3068        '''
3069       
3070        if self.GSASprojectfile: 
3071            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3072            self.CheckNotebook()
3073            G2IO.ProjFileSave(self)
3074        else:
3075            self.OnFileSaveas(event)
3076
3077    def OnFileSaveas(self, event):
3078        '''Save the current project in response to the
3079        File/Save as menu button
3080        '''
3081        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
3082            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3083        try:
3084            if dlg.ShowModal() == wx.ID_OK:
3085                self.GSASprojectfile = dlg.GetPath()
3086                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3087                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3088                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3089                self.CheckNotebook()
3090                G2IO.ProjFileSave(self)
3091                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3092        finally:
3093            dlg.Destroy()
3094
3095    def ExitMain(self, event):
3096        '''Called if the main window is closed'''
3097        if self.undofile:
3098            os.remove(self.undofile)
3099        sys.exit()
3100       
3101    def OnFileExit(self, event):
3102        '''Called in response to the File/Quit menu button'''
3103        if self.dataFrame:
3104            self.dataFrame.Clear() 
3105            self.dataFrame.Destroy()
3106        self.Close()
3107       
3108    def OnExportPeakList(self,event):
3109        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
3110            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3111        try:
3112            if dlg.ShowModal() == wx.ID_OK:
3113                self.peaklistfile = dlg.GetPath()
3114                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3115                file = open(self.peaklistfile,'w')               
3116                item, cookie = self.PatternTree.GetFirstChild(self.root)
3117                while item:
3118                    name = self.PatternTree.GetItemText(item)
3119                    if 'PWDR' in name:
3120                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3121                        while item2:
3122                            name2 = self.PatternTree.GetItemText(item2)
3123                            if name2 == 'Peak List':
3124                                peaks = self.PatternTree.GetItemPyData(item2)['peaks']
3125                                file.write("%s \n" % (name+' Peak List'))
3126                                if len(peaks[0]) == 8:
3127                                    file.write('%10s %12s %10s %10s %10s\n'%('pos','int','sig','gam','FWHM'))
3128                                else:
3129                                    file.write('%10s %12s %10s %10s %10s %10s %10s\n'%('pos','int','alp','bet','sig','gam','FWHM'))                                   
3130                                for peak in peaks:
3131                                    if len(peak) == 8:  #CW
3132                                        FWHM = 2.*G2pwd.getgamFW(peak[6],peak[4])      #to get delta-2-theta in deg. from Gam(peak)
3133                                        file.write("%10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3134                                            (peak[0],peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3135                                    else:               #TOF - more cols
3136                                        FWHM = 2.*G2pwd.getgamFW(peak[10],peak[8])      #to get delta-TOF from Gam(peak)
3137                                        file.write("%10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3138                                            (peak[0],peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3139                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3140                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3141                file.close()
3142        finally:
3143            dlg.Destroy()
3144       
3145    def OnExportHKL(self,event):
3146        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
3147            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3148        try:
3149            if dlg.ShowModal() == wx.ID_OK:
3150                self.peaklistfile = dlg.GetPath()
3151                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3152                file = open(self.peaklistfile,'w')               
3153                item, cookie = self.PatternTree.GetFirstChild(self.root)
3154                while item:
3155                    name = self.PatternTree.GetItemText(item)
3156                    if 'PWDR' in name:
3157                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3158                        while item2:
3159                            name2 = self.PatternTree.GetItemText(item2)
3160                            if name2 == 'Reflection Lists':
3161                                data = self.PatternTree.GetItemPyData(item2)
3162                                phases = data.keys()
3163                                for phase in phases:
3164                                    peaks = data[phase]
3165                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3166                                    if 'T' in peaks.get('Type','PXC'):
3167                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3168                                    else:               
3169                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3170                                    for peak in peaks['RefList']:
3171                                        FWHM = 2.*G2pwd.getgamFW(peak[7],peak[6])
3172                                        if 'T' in peaks.get('Type','PXC'):
3173                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3174                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3175                                        else:
3176                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3177                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3178                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3179                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3180                file.close()
3181        finally:
3182            dlg.Destroy()
3183       
3184    def OnExportPDF(self,event):
3185        #need S(Q) and G(R) to be saved here - probably best from selection?
3186        names = ['All']
3187        exports = []
3188        item, cookie = self.PatternTree.GetFirstChild(self.root)
3189        while item:
3190            name = self.PatternTree.GetItemText(item)
3191            if 'PDF' in name:
3192                names.append(name)
3193            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3194        if names:
3195            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3196            if dlg.ShowModal() == wx.ID_OK:
3197                sel = dlg.GetSelections()
3198                if sel[0] == 0:
3199                    exports = names[1:]
3200                else:
3201                    for x in sel:
3202                        exports.append(names[x])
3203            dlg.Destroy()
3204        if exports:
3205            G2IO.PDFSave(self,exports)
3206       
3207    def OnMakePDFs(self,event):
3208        '''Calculates PDFs
3209        '''
3210        sind = lambda x: math.sin(x*math.pi/180.)
3211        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3212        TextList = ['All PWDR']
3213        PDFlist = []
3214        Names = []
3215        if self.PatternTree.GetCount():
3216            id, cookie = self.PatternTree.GetFirstChild(self.root)
3217            while id:
3218                name = self.PatternTree.GetItemText(id)
3219                Names.append(name)
3220                if 'PWDR' in name:
3221                    TextList.append(name)
3222                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3223            if len(TextList) == 1:
3224                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3225                return
3226            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3227            try:
3228                if dlg.ShowModal() == wx.ID_OK:
3229                    result = dlg.GetSelections()
3230                    for i in result: PDFlist.append(TextList[i])
3231                    if 0 in result:
3232                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3233                    for item in PDFlist:
3234                        PWDRname = item[4:]
3235                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3236                        Data = {
3237                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3238                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3239                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3240                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3241                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3242                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3243                            'Lorch':True,}
3244                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3245                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3246                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3247                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3248                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3249                for item in self.ExportPDF: item.Enable(True)
3250            finally:
3251                dlg.Destroy()
3252               
3253    def GetPWDRdatafromTree(self,PWDRname):
3254        ''' Returns powder data from GSASII tree
3255
3256        :param str PWDRname: a powder histogram name as obtained from
3257          :meth:`GSASIIstruct.GetHistogramNames`
3258
3259        :returns: PWDRdata = powder data dictionary with
3260          Powder data arrays, Limits, Instrument Parameters,
3261          Sample Parameters           
3262        '''
3263        PWDRdata = {}
3264        try:
3265            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3266        except ValueError:
3267            PWDRdata['wtFactor'] = 1.0
3268        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3269        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3270        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3271        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3272        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3273        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3274        if 'ranId' not in PWDRdata:  # patch, add a random Id
3275            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3276        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3277            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3278        return PWDRdata
3279
3280    def GetHKLFdatafromTree(self,HKLFname):
3281        ''' Returns single crystal data from GSASII tree
3282
3283        :param str HKLFname: a single crystal histogram name as obtained
3284          from
3285          :meth:`GSASIIstruct.GetHistogramNames`
3286
3287        :returns: HKLFdata = single crystal data list of reflections
3288
3289        '''
3290        HKLFdata = {}
3291        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3292#        try:
3293#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3294#        except ValueError:
3295#            HKLFdata['wtFactor'] = 1.0
3296        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3297        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3298        return HKLFdata
3299       
3300    def GetPhaseData(self):
3301        '''Returns a dict with defined phases.
3302        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3303        get same info from GPX file.
3304        '''
3305        phaseData = {}
3306        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3307            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3308        else:
3309            print 'no phases found in GetPhaseData'
3310            sub = None
3311        if sub:
3312            item, cookie = self.PatternTree.GetFirstChild(sub)
3313            while item:
3314                phaseName = self.PatternTree.GetItemText(item)
3315                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3316                if 'ranId' not in phaseData[phaseName]:
3317                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3318                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3319        return phaseData
3320
3321    def GetPhaseInfofromTree(self):
3322        '''Get the phase names and their rId values,
3323        also the histograms used in each phase.
3324
3325        :returns: (phaseRIdList, usedHistograms) where
3326
3327          * phaseRIdList is a list of random Id values for each phase
3328          * usedHistograms is a dict where the keys are the phase names
3329            and the values for each key are a list of the histogram names
3330            used in each phase.
3331        '''
3332        phaseRIdList = []
3333        usedHistograms = {}
3334        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3335        if sub:
3336            item, cookie = self.PatternTree.GetFirstChild(sub)
3337            while item:
3338                phaseName = self.PatternTree.GetItemText(item)
3339                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3340                if ranId: phaseRIdList.append(ranId)
3341                data = self.PatternTree.GetItemPyData(item)
3342                UseList = data['Histograms']
3343                usedHistograms[phaseName] = UseList.keys()
3344                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3345        return phaseRIdList,usedHistograms
3346
3347    def GetPhaseNames(self):
3348        '''Returns a list of defined phases.
3349        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3350        get same info from GPX file.
3351        '''
3352        phaseNames = []
3353        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3354            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3355        else:
3356            print 'no phases found in GetPhaseNames'
3357            sub = None
3358        if sub:
3359            item, cookie = self.PatternTree.GetFirstChild(sub)
3360            while item:
3361                phase = self.PatternTree.GetItemText(item)
3362                phaseNames.append(phase)
3363                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3364        return phaseNames
3365   
3366    def GetHistogramNames(self,hType):
3367        """ Returns a list of histogram names found in the GSASII data tree
3368        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3369        get same info from GPX file.
3370       
3371        :param str hType: list of histogram types
3372        :return: list of histogram names
3373       
3374        """
3375        HistogramNames = []
3376        if self.PatternTree.GetCount():
3377            item, cookie = self.PatternTree.GetFirstChild(self.root)
3378            while item:
3379                name = self.PatternTree.GetItemText(item)
3380                if name[:4] in hType:
3381                    HistogramNames.append(name)       
3382                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3383
3384        return HistogramNames
3385                   
3386    def GetUsedHistogramsAndPhasesfromTree(self):
3387        ''' Returns all histograms that are found in any phase
3388        and any phase that uses a histogram.
3389        This also assigns numbers to used phases and histograms by the
3390        order they appear in the file.
3391        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3392        get same info from GPX file.
3393
3394        :returns: (Histograms,Phases)
3395
3396            * Histograms = dictionary of histograms as {name:data,...}
3397            * Phases = dictionary of phases that use histograms
3398        '''
3399        Histograms = {}
3400        Phases = {}
3401        phaseNames = self.GetPhaseNames()
3402        phaseData = self.GetPhaseData()
3403        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3404
3405        for phase in phaseData:
3406            Phase = phaseData[phase]
3407            pId = phaseNames.index(phase)
3408            Phase['pId'] = pId
3409            if Phase['Histograms']:
3410                if phase not in Phases:
3411                    Phases[phase] = Phase
3412                for hist in Phase['Histograms']:
3413                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3414                        Phase['Histograms'][hist]['Use'] = True         
3415                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3416                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3417                        if item:
3418                            if 'PWDR' in hist[:4]: 
3419                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3420                            elif 'HKLF' in hist[:4]:
3421                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3422                            hId = histoList.index(hist)
3423                            Histograms[hist]['hId'] = hId
3424                        else: # would happen if a referenced histogram were renamed or deleted
3425                            print('For phase "'+str(phase)+
3426                                  '" unresolved reference to histogram "'+str(hist)+'"')
3427        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3428        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3429        return Histograms,Phases
3430       
3431    def MakeLSParmDict(self):
3432        '''Load all parameters used for computation from the tree into a
3433        dict of paired values [value, refine flag]. Note that this is
3434        different than the parmDict used in the refinement, which only has
3435        values.
3436
3437        Note that similar things are done in
3438        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3439        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3440        a GPX file).
3441
3442        :returns: (parmDict,varyList) where:
3443
3444         * parmDict is a dict with values and refinement flags
3445           for each parameter and
3446         * varyList is a list of variables (refined parameters).
3447        '''
3448        parmDict = {}
3449        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3450        for phase in Phases:
3451            if 'pId' not in Phases[phase]:
3452                self.ErrorDialog('View parameter error','You must run least squares at least once')
3453                raise Exception,'No pId for phase '+str(phase)
3454        rigidbodyDict = self.PatternTree.GetItemPyData(   
3455            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3456        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3457        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3458        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3459        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3460        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3461        varyList = rbVary+phaseVary+hapVary+histVary
3462        parmDict.update(rbDict)
3463        parmDict.update(phaseDict)
3464        parmDict.update(hapDict)
3465        parmDict.update(histDict)
3466        for parm in parmDict:
3467            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3468                'Omega','Chi','Phi','nDebye','nPeaks']:
3469                parmDict[parm] = [parmDict[parm],'-']
3470            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3471                parmDict[parm] = [parmDict[parm],'-']
3472            elif parm in varyList:
3473                parmDict[parm] = [parmDict[parm],'T']
3474            else:
3475                parmDict[parm] = [parmDict[parm],'F']
3476        # for i in parmDict: print i,'\t',parmDict[i]
3477        # fl = open('parmDict.dat','wb')
3478        # import cPickle
3479        # cPickle.dump(parmDict,fl,1)
3480        # fl.close()
3481        return parmDict,varyList
3482
3483    def ShowLSParms(self,event):
3484        '''Displays a window showing all parameters in the refinement.
3485        Called from the Calculate/View LS Parms menu.
3486        '''
3487        parmDict,varyList = self.MakeLSParmDict()
3488        parmValDict = {}
3489        for i in parmDict:
3490            parmValDict[i] = parmDict[i][0]
3491           
3492        reqVaryList = tuple(varyList) # save requested variables
3493        try:
3494            # process constraints
3495            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3496            Constraints = self.PatternTree.GetItemPyData(sub)
3497            constList = []
3498            for item in Constraints:
3499                if item.startswith('_'): continue
3500                constList += Constraints[item]
3501            G2mv.InitVars()
3502            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3503            groups,parmlist = G2mv.GroupConstraints(constrDict)
3504            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3505            G2mv.Map2Dict(parmValDict,varyList)
3506        except:
3507            pass
3508        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3509        dlg.ShowModal()
3510        dlg.Destroy()
3511       
3512    def OnRefine(self,event):
3513        '''Perform a refinement.
3514        Called from the Calculate/Refine menu.
3515        '''       
3516        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3517        if Id:
3518            dlg = wx.MessageDialog(
3519                self,
3520                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3521                'Remove sequential results?',wx.OK|wx.CANCEL)
3522            if dlg.ShowModal() == wx.ID_OK:
3523                self.PatternTree.Delete(Id)
3524                dlg.Destroy()
3525            else:
3526                dlg.Destroy()
3527                return
3528        self.OnFileSave(event)
3529        # check that constraints are OK here
3530        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3531        if errmsg:
3532            self.ErrorDialog('Refinement error',errmsg)
3533            return
3534        if warnmsg:
3535            print('Conflict between refinment flag settings and constraints:\n'+
3536                warnmsg+'\nRefinement not possible')
3537            self.ErrorDialog('Refinement Flag Error',
3538                'Conflict between refinement flag settings and constraints:\n'+
3539                warnmsg+'\nRefinement not possible')
3540            return
3541        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3542            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3543            parent=self)
3544        Size = dlg.GetSize()
3545        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3546            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3547        dlg.CenterOnParent()
3548        Rw = 100.00
3549        oldId =  self.PatternTree.GetSelection()        #retain current selection
3550        oldPath = self.GetTreeItemsList(oldId)
3551        parentName = ''
3552        oldName = self.PatternTree.GetItemText(oldId)
3553        parentId = self.PatternTree.GetItemParent(oldId)
3554        if parentId:
3555            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3556            if 'Phases' in parentName:
3557                tabId = self.dataDisplay.GetSelection()
3558        try:
3559            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)
3560        finally:
3561            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3562            dlg.Destroy()
3563            wx.Yield()
3564        if OK:
3565            Rw = Msg
3566            dlg2 = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3567            try:
3568                if dlg2.ShowModal() == wx.ID_OK:
3569                    Id = 0
3570                    self.PatternTree.DeleteChildren(self.root)
3571                    self.HKL = []
3572                    G2IO.ProjFileOpen(self)
3573                    Id =  self.root
3574                    txt = None
3575                    for txt in oldPath:
3576                        Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3577                    self.PickIdText = None  #force reload of page
3578                    self.PickId = Id
3579                    self.PatternTree.SelectItem(Id)
3580                    G2gd.MovePatternTreeToGrid(self,Id)
3581            finally:
3582                dlg2.Destroy()
3583        else:
3584            self.ErrorDialog('Refinement error',Msg)
3585
3586    def OnSeqRefine(self,event):
3587        '''Perform a sequential refinement.
3588        Called from the Calculate/Sequential refine menu.
3589        '''       
3590        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3591        if not Id:
3592            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3593            self.PatternTree.SetItemPyData(Id,{})           
3594        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3595        Controls['ShowCell'] = True
3596        self.OnFileSave(event)
3597        # check that constraints are OK here
3598        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3599        if errmsg:
3600            self.ErrorDialog('Refinement error',errmsg)
3601            return
3602        if warnmsg:
3603            print('Conflict between refinment flag settings and constraints:\n'+
3604                  warnmsg+'\nRefinement not possible')
3605            self.ErrorDialog('Refinement Flag Error',
3606                             'Conflict between refinment flag settings and constraints:\n'+
3607                             warnmsg+'\nRefinement not possible')
3608            return
3609        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3610            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3611            parent=self)           
3612        Size = dlg.GetSize()
3613        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3614            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3615        dlg.CenterOnParent()
3616        try:
3617            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3618        finally:
3619            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3620            dlg.Destroy()
3621            wx.Yield()
3622        if OK:
3623            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3624            try:
3625                if dlg.ShowModal() == wx.ID_OK:
3626                    Id = 0
3627                    self.PickIdText = None  #force reload of PickId contents
3628                    self.PatternTree.DeleteChildren(self.root)
3629                    if len(self.HKL): self.HKL = []
3630                    if self.G2plotNB.plotList:
3631                        self.G2plotNB.clear()
3632                    G2IO.ProjFileOpen(self)
3633                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3634                    self.PatternTree.SelectItem(Id)
3635   
3636            finally:
3637                dlg.Destroy()
3638        else:
3639            self.ErrorDialog('Sequential refinement error',Msg)
3640       
3641    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3642        'Display an error message'
3643        result = None
3644        if parent is None:
3645            dlg = wx.MessageDialog(self, message, title,  wtype)
3646        else:
3647            dlg = wx.MessageDialog(parent, message, title,  wtype)
3648            dlg.CenterOnParent() # not working on Mac
3649        try:
3650            result = dlg.ShowModal()
3651        finally:
3652            dlg.Destroy()
3653        return result
3654
3655class GSASIImain(wx.App):
3656    '''Defines a wxApp for GSAS-II
3657
3658    Creates a wx frame (self.main) which contains the display of the
3659    data tree.
3660    '''
3661    def OnInit(self):
3662        '''Called automatically when the app is created.'''
3663        if '2.7' not in sys.version[:5]:
3664            dlg = wx.MessageDialog(None, 
3665                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
3666                'Python version error',  wx.OK)
3667            try:
3668                result = dlg.ShowModal()
3669            finally:
3670                dlg.Destroy()
3671            sys.exit()
3672        self.main = GSASII(None)
3673        self.main.Show()
3674        self.SetTopWindow(self.main)
3675        # save the current package versions
3676        self.main.PackageVersions = []
3677        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
3678        for p in (wx,mpl,np,sp,ogl):
3679            self.main.PackageVersions.append([p.__name__,p.__version__])
3680        try:
3681            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
3682        except:
3683            try:
3684                from PIL import PILLOW_VERSION
3685                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
3686            except:
3687                pass
3688        self.main.PackageVersions.append([' Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
3689       
3690#        self.main.PackageVersions = {}
3691#        self.main.PackageVersions['Python'] = sys.version.split()[0]
3692#        for p in (wx,mpl,np,sp,ogl):
3693#            self.main.PackageVersions[p.__name__] = p.__version__
3694#        try:
3695#            self.main.PackageVersions[Image.__name__] = Image.VERSION
3696#        except:
3697#            try:
3698#                from PIL import PILLOW_VERSION
3699#                self.main.PackageVersions[Image.__name__] = PILLOW_VERSION
3700#            except:
3701#                pass
3702#        self.main.PackageVersions[' Platform'] = sys.platform+' '+platform.architecture()[0]+' '+platform.machine()
3703        # DEBUG: jump to sequential results
3704        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3705        #self.main.PatternTree.SelectItem(Id)
3706        # end DEBUG
3707        return True
3708    # def MacOpenFile(self, filename):
3709    #     '''Called on Mac every time a file is dropped on the app when it is running,
3710    #     treat this like a File/Open project menu action.
3711    #     Should be ignored on other platforms
3712    #     '''
3713    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3714    #     print 'MacOpen',filename
3715    #     if os.path.splitext(filename)[1] == '.py': return
3716    #     # end PATCH
3717    #     self.main.OnFileOpen(None,filename)
3718    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3719       
3720def main():
3721    '''Start up the GSAS-II application'''
3722    #application = GSASIImain() # don't redirect output, someday we
3723    # may want to do this if we can
3724    application = GSASIImain(0)
3725    if GSASIIpath.GetConfigValue('wxInspector'):
3726        import wx.lib.inspection as wxeye
3727        wxeye.InspectionTool().Show()
3728
3729    #application.main.OnRefine(None)
3730    application.MainLoop()
3731   
3732if __name__ == '__main__':
3733    # print versions
3734    print "Python module versions loaded:"
3735    print "python:     ",sys.version.split()[0]
3736    print "wxpython:   ",wx.__version__
3737    print "matplotlib: ",mpl.__version__
3738    print "numpy:      ",np.__version__
3739    print "scipy:      ",sp.__version__
3740    print "OpenGL:     ",ogl.__version__
3741    try:
3742        from PIL import Image
3743        try:
3744            from PIL import PILLOW_VERSION
3745            version = PILLOW_VERSION
3746        except:
3747            version = Image.VERSION
3748        print "pillow:     ",version
3749    except ImportError:
3750        try:
3751            import Image
3752            print "Image (PIL):",Image.VERSION
3753        except ImportError:
3754            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3755    try:
3756        import mkl
3757        print "Max threads ",mkl.get_max_threads()
3758    except:
3759        pass
3760    import platform
3761    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3762    #print "wxPython description",wx.PlatformInfo
3763    print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
3764    GSASIIpath.InvokeDebugOpts()
3765    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.