source: trunk/GSASII.py @ 2056

Last change on this file since 2056 was 2056, checked in by vondreele, 7 years ago

peak list read/list/indexing now fixed

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