source: trunk/GSASII.py @ 1985

Last change on this file since 1985 was 1985, checked in by toby, 6 years ago

fix CIF import where ? is used for Uiso value and where iso/aniso type is inferred

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