Changeset 4785 for trunk


Ignore:
Timestamp:
Jan 27, 2021 3:27:25 PM (9 months ago)
Author:
toby
Message:

new table for text w/sorting

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/G2compare.py

    r4364 r4785  
    1818#TODO:
    1919# Prince-test next
    20 # Make PWDR unique? (use histlist)
     20# Make Phase unique? (need a phaselist)
    2121# more graphics
    2222# display more in datawindow
     
    2525import os
    2626import platform
     27import glob
    2728if '2' in platform.python_version_tuple()[0]:
    2829    import cPickle
     
    4748import GSASIIfiles as G2fil
    4849import GSASIIplot as G2plt
     50import GSASIIdataGUI as G2gd
    4951import GSASIIctrlGUI as G2G
    5052import GSASIIobj as G2obj
     
    105107def main(application):
    106108    '''Start up the GSAS-II GUI'''                       
    107     knownVersions = ['2.7','3.6','3.7','3.8']
     109    knownVersions = ['3.6','3.7','3.8','3.9']
    108110    if platform.python_version()[:3] not in knownVersions:
    109111        dlg = wx.MessageDialog(None,
    110                 'GSAS-II requires Python 2.7.x or 3.6+\n Yours is '+sys.version.split()[0],
     112                'GSAS-II requires Python 3.6+\n Yours is '+sys.version.split()[0],
    111113                'Python version error',  wx.OK)
    112114        try:
     
    158160        File = wx.Menu(title='')
    159161        self.MenuBar.Append(menu=File, title='&File')
    160         item = File.Append(wx.ID_ANY,'&Import project...\tCtrl+O','Open a GSAS-II project file (*.gpx)')           
     162        item = File.Append(wx.ID_ANY,'&Import single project...\tCtrl+O','Open a GSAS-II project file (*.gpx)')           
    161163        self.Bind(wx.EVT_MENU, self.onLoadGPX, id=item.GetId())
     164        item = File.Append(wx.ID_ANY,'&Import multiple projects...\tCtrl+M','Open a GSAS-II project file (*.gpx)')           
     165        self.Bind(wx.EVT_MENU, self.onLoadMultGPX, id=item.GetId())
     166        item = File.Append(wx.ID_ANY,'&Wildcard import of projects...\tCtrl+W','Open a GSAS-II project file (*.gpx)')           
     167        self.Bind(wx.EVT_MENU, self.onLoadWildGPX, id=item.GetId())
    162168#        item = File.Append(wx.ID_ANY,'&Import selected...','Open a GSAS-II project file (*.gpx)')
    163169#        self.Bind(wx.EVT_MENU, self.onLoadSel, id=item.GetId())
     
    166172        self.MenuBar.Append(menu=self.Mode, title='&Mode')
    167173        self.wxID_Mode = {}
    168         for m in "Histogram","Phase","Project":
    169             i = self.wxID_Mode[m] = wx.NewId()
    170             item = self.Mode.AppendRadioItem(i,m,'Display {}s'.format(m))
     174        for i,m in enumerate(("Histogram","Phase","Project")):
     175            self.wxID_Mode[m] = i+1
     176            item = self.Mode.AppendRadioItem(i+1,m+'\tCtrl+{}'.format(i+1),
     177                                                 'Display {}s'.format(m))
    171178            self.Bind(wx.EVT_MENU, self.onRefresh, id=item.GetId())
    172179        item = self.Mode.Append(wx.ID_ANY,'Set histogram filter','Set a filter for histograms to display')
     
    198205        self.GPXtree = G2G.G2TreeCtrl(id=wx.ID_ANY,
    199206            parent=self.treePanel, size=self.treePanel.GetClientSize(),style=wx.TR_DEFAULT_STYLE )
    200         TreeId = self.GPXtree.Id
     207        #TreeId = self.GPXtree.Id
    201208
    202209        treeSizer.Add(self.GPXtree,1,wx.EXPAND|wx.ALL,0)
     
    221228        self.fileList = []  # list of files read for use in Reload
    222229        self.histList = []  # list of histograms loaded for unique naming
     230        self.projList = []
    223231
    224232        self.PWDRfilter = None
     
    229237                if type(pos) is str: pos = eval(pos)
    230238                win.SetPosition(pos)
    231                 if GetDisplay(pos) is None: win.Center()
     239                if G2gd.GetDisplay(pos) is None: win.Center()
    232240            except:
    233241                if GSASIIpath.GetConfigValue(var):
     
    247255            dlg.Destroy()
    248256        if os.path.exists(fil):
    249             self.fileList.append([fil,'GPX'])
     257            #self.fileList.append([fil,'GPX'])
    250258            return fil
    251259        else:
    252260            print('File {} not found, skipping'.format(fil))
    253261            return
     262
     263    def SelectMultGPX(self):
     264        '''Select multiple .GPX files to be read
     265        '''
     266        dlg = wx.FileDialog(self, 'Choose GSAS-II project file',
     267                wildcard='GSAS-II project file (*.gpx)|*.gpx',
     268                style=wx.FD_OPEN|wx.FD_MULTIPLE)
     269        try:
     270            if dlg.ShowModal() != wx.ID_OK: return
     271            files = dlg.GetPaths()
     272        finally:
     273            dlg.Destroy()
     274        fileList = []
     275        for f in files:
     276            fil = os.path.splitext(f)[0]+'.gpx'
     277            if os.path.exists(fil):
     278                if fil not in fileList:
     279                    fileList.append(fil)
     280            else:
     281                print('File {} not found, skipping'.format(fil))
     282        return fileList
    254283       
    255284    def getMode(self):
     
    269298        self.GPXtree.DeleteChildren(self.root)  # delete tree contents
    270299        self.histList = []  # clear list of loaded histograms
     300        self.projList = []
    271301        for fil,mode in self.fileList:
    272302            self.loadFile(fil)
     303        self.doneLoad()
    273304        self.SetModeMenu()
    274305           
     
    300331            print("mode not implemented")
    301332            #raise Exception("mode not implemented")
    302        
     333
     334    def doneLoad(self):
     335        self.GPXtree.Expand(self.root)
     336        if self.getMode() == "Project":
     337            overId = self.GPXtree.InsertItem(pos=0,parent=self.root,text='Project Overview')
     338            self.GPXtree.SelectItem(overId)
     339
    303340    def onLoadGPX(self,event):
    304341        '''Initial load of GPX file in response to a menu command
     
    309346        self.fileList.append([fil,'GPX'])
    310347        self.loadFile(fil)
     348        self.doneLoad()
     349
     350    def onLoadMultGPX(self,event):
     351        '''Initial load of multiple GPX files in response to a menu command
     352        '''
     353        for fil in self.SelectMultGPX():
     354            if not os.path.exists(fil): continue
     355            self.fileList.append([fil,'GPX'])
     356            self.loadFile(fil)
     357        self.doneLoad()
     358
     359    def onLoadWildGPX(self,event,wildcard=None):
     360        '''Initial load of GPX file in response to a menu command
     361        '''
     362        home = os.path.abspath(os.getcwd())
     363        hlp = '''Enter a wildcard version of a file name.
     364The directory is assumed to be "{}" unless specified otherwise.
     365Extensions will be set to .gpx and .bak files will be ignored unless
     366the wildcard string includes "bak". For example, "*abc*" will match any
     367.gpx file in that directory containing "abc". String "/tmp/A*" will match
     368files in "/tmp" beginning with "A". Supplying two strings, "A*" and "B*bak*"
     369will match files names beginning with "A" or "B", but ".bak" files will
     370be included for the files beginning with "B" only.
     371'''.format(home)
     372        if wildcard is None:
     373            dlg = G2G.MultiStringDialog(self, 'Enter wildcard file names',
     374                    ['wild-card 1'] , values=['*'],
     375                    lbl='Provide string(s) with "*" to find matching files',
     376                    addRows=True, hlp=hlp)
     377            res = dlg.Show()
     378            wl = dlg.GetValues()
     379            dlg.Destroy()
     380            if not res: return
     381        else:
     382            wl = [wildcard]
     383        for w in wl:
     384            if not os.path.split(w)[0]:
     385                w = os.path.join(home,w)
     386            w = os.path.splitext(w)[0] + '.gpx'
     387            for fil in glob.glob(w):
     388                if not os.path.exists(fil): continue
     389                if '.bak' in fil and 'bak' not in w: continue
     390                if fil in [i for i,j in self.fileList]: continue
     391                self.fileList.append([fil,'GPX'])
     392                self.loadFile(fil)
     393        self.doneLoad()
    311394
    312395    def LoadPwdr(self,fil):
     
    369452                G2frame.GPXtree.SetItemPyData(sub,datus[1])
    370453        if datum: # was anything loaded?
    371             print('project load successful for {}'.format(datum[0]))
     454            print('data load successful for {}'.format(datum[0]))
    372455    #        G2frame.Status.SetStatusText('Mouse RB drag/drop to reorder',0)
    373456    #    G2frame.SetTitleByGPX()
     
    449532        if datum: # was anything loaded?
    450533            self.GPXtree.Expand(Id)
    451             print('project load successful for {}'.format(datum[0]))
     534            print('Phase load successful for {}'.format(datum[0]))
    452535    #        G2frame.Status.SetStatusText('Mouse RB drag/drop to reorder',0)
    453536    #    G2frame.SetTitleByGPX()
     
    458541        see :func:`GSASIIIO.ProjFileOpen`
    459542        '''
     543        import datetime
    460544        G2frame = self
    461545        filep = open(fil,'rb')
     546        saved = datetime.datetime.fromtimestamp(os.path.getmtime(fil)).strftime("%Y-%h-%d %H:%M")
    462547        shortname = os.path.splitext(os.path.split(fil)[1])[0]
    463 
     548        projInfo = [shortname,saved]
    464549        wx.BeginBusyCursor()
    465         Phases = None
     550        #Phases = None
     551        #G2frame.GPXtree.SetItemPyData(Id,Covar[1])
    466552        try:
    467553            while True:
     
    470556                except EOFError:
    471557                    break
     558                #if data[0][0] == 'Controls' and 'LastSavedUsing' in data[0][1]:
    472559                if not data[0][0].startswith('Covariance'): continue
    473560                Covar = data[0]
     561                f = '{:d}'
     562                if 'varyList' in data[0][1]:
     563                    projInfo += [f.format(len(data[0][1]['varyList']))]
     564                else:
     565                    projInfo += ['?']
     566                for v in 'Nobs','GOF':
     567                    if 'Rvals' in data[0][1] and v in data[0][1]['Rvals']:
     568                        projInfo += [f.format(data[0][1]['Rvals'][v])]
     569                    else:
     570                        projInfo += ['?']
     571                    f = '{:6.2f}'
    474572                #GSASIIpath.IPyBreak_base()
    475573                #if self.PWDRfilter is not None: # implement filter
    476574                #    if self.PWDRfilter not in data[0][0]: continue
    477                 Covar[0] = shortname + ' Covariance'
     575                Covar[0] = shortname # + ' Covariance'
    478576                Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text=Covar[0])
    479577                G2frame.GPXtree.SetItemPyData(Id,Covar[1])
     578                self.projList.append(projInfo)
    480579                break
    481580            else:
     
    493592            filep.close()
    494593            wx.EndBusyCursor()
    495         self.GPXtree.Expand(self.root)
    496594
    497595    def OnDataTreeSelChanged(self,event):
     
    510608        item = event.GetItem()
    511609        #print('selected',item)
    512         if self.getMode() == "Project":
     610        lbl = G2frame.GPXtree.GetItemText(item)
     611        if self.getMode() == "Project" and lbl == 'Project Overview':
     612            ClearData(G2frame.dataWindow)
     613            #import imp
     614            #imp.reload(G2G)
     615            pnl = G2G.SortableLstCtrl(G2frame.dataWindow)
     616            h = ["File", "last saved", "vars", "Nobs", "GOF"]
     617            j = [ 0,       0,           1,      1,      1]
     618            pnl.PopulateHeader(h,j)
     619            for i,line in enumerate(self.projList):
     620                pnl.PopulateLine(i,line)
     621            G2frame.dataWindow.GetSizer().Add(pnl,1,wx.EXPAND)
     622            pnl.SetColWidth(0,maxwidth=170)
     623            for i in range(1,len(h)):
     624                pnl.SetColWidth(i,minwidth=50)
     625            G2frame.dataWindow.SendSizeEvent()
     626        elif self.getMode() == "Project":
    513627            ClearData(G2frame.dataWindow)
    514628            data = G2frame.GPXtree.GetItemPyData(item)
     
    528642                if 'lamMax' in Rvals:
    529643                    text += '\n\tlog10 MaxLambda = {:.1f}'.format(np.log10(Rvals['lamMax']))
     644                text += '\n\tReduced χ**2 = {:.2f}'.format(Rvals['GOF']**2)
    530645                G2frame.dataWindow.GetSizer().Add(
    531646                    wx.StaticText(G2frame.dataWindow,wx.ID_ANY,text)
     
    722837            G2G.G2MessageBox(self,'Unable to use test: "X" values differ','Comparison not valid')
    723838            return
    724         X = data0[3] - data1[3]
    725         #Z = np.sqrt(data0[3]) * (data0[1] - (data0[3] + data1[3])/2)
    726         X = (data0[3] - data1[3]) / np.sqrt(data0[1])
    727         Z = (data0[1] - (data0[3] + data1[3])/2) / np.sqrt(data0[1])
    728         lam = np.sum(X*Z) / np.sum(X)
    729         sig = np.sqrt(
    730             (np.sum(Z*Z) - lam*lam*np.sum(X*X)) /
    731             ((len(data0[0]) - 1) * np.sum(X*X))
    732             )
     839        # X = data0[3] - data1[3]
     840        # #Z = np.sqrt(data0[3]) * (data0[1] - (data0[3] + data1[3])/2)
     841        # X = (data0[3] - data1[3]) / np.sqrt(data0[1])
     842        # Z = (data0[1] - (data0[3] + data1[3])/2) / np.sqrt(data0[1])
     843        # lam = np.sum(X*Z) / np.sum(X)
     844        # sig = np.sqrt(
     845        #     (np.sum(Z*Z) - lam*lam*np.sum(X*X)) /
     846        #     ((len(data0[0]) - 1) * np.sum(X*X))
     847        #     )
    733848           
    734849#    0 the x-postions (two-theta in degrees),
     
    752867        print('Unable to run with current setup, do you want to update to the')
    753868        try:
    754             if '2' in platform.python_version_tuple()[0]:           
    755                 ans = raw_input("latest GSAS-II version? Update ([Yes]/no): ")
    756             else:
     869#            if '2' in platform.python_version_tuple()[0]:           
     870#                ans = raw_input("latest GSAS-II version? Update ([Yes]/no): ")
     871#            else:
    757872                ans = input("latest GSAS-II version? Update ([Yes]/no): ")               
    758873        except:
    759874            ans = 'no'
    760875        if ans.strip().lower() == "no":
    761             import sys
    762876            print('Exiting')
    763877            sys.exit()
     
    774888        else:
    775889            print('File {} not found. Skipping'.format(fil))
    776 
    777     # debug code to select in initial mode
    778     #self=Frame
    779     #self.Mode.FindItemById(self.wxID_Mode['Project']).Check(True)
    780     #self.onRefresh(None)
     890    Frame.doneLoad()
     891    # debug code to select Project in initial mode
     892    #Frame.onLoadWildGPX(None,wildcard='*')
     893    #Frame.Mode.FindItemById(Frame.wxID_Mode['Project']).Check(True)
     894    #Frame.onRefresh(None)
    781895   
    782896    application.MainLoop()
  • trunk/GSASIIctrlGUI.py

    r4783 r4785  
    5757:class:`OrderBox`                  Creates a wx.Panel with scrollbars where items can be
    5858                                   ordered into columns.
     59:class:`SortableLstCtrl`           Creates a wx.Panel for a table of data that 
     60                                   can be sorted by clicking on a column label.
    5961:class:`ScrolledMultiEditor`       wx.Dialog for editing many dict- or list-contained items.
    6062                                   with validation. Results are placed in dict or list.
     
    27842786            hlp = HelpButton(self, self.hlp, wrap=450)
    27852787            btnsizer.Add((-1,-1),1, wx.EXPAND, 1)
    2786             btnsizer.Add(hlp,0,wx.ALIGN_RIGHT|wx.ALL)
     2788            #btnsizer.Add(hlp,0,wx.ALIGN_RIGHT|wx.ALL)
     2789            btnsizer.Add(hlp,0)
    27872790            mainSizer.Add(btnsizer,0,wx.EXPAND)
    27882791        if self.lbl:
    2789             mainSizer.Add(wx.StaticText(self,wx.ID_ANY,self.lbl),0,WACV,0)
     2792            mainSizer.Add(wx.StaticText(self,wx.ID_ANY,self.lbl))
    27902793            mainSizer.Add((-1,15))
    27912794        promptSizer = wx.FlexGridSizer(0,2,5,5)
     
    27932796        self.Indx = {}
    27942797        for prompt,value in zip(self.prompts,self.values):
    2795             promptSizer.Add(wx.StaticText(self,-1,prompt),0,WACV,0)
     2798            promptSizer.Add(wx.StaticText(self,-1,prompt))
    27962799            valItem = wx.TextCtrl(self,-1,value=value,style=wx.TE_PROCESS_ENTER,size=(self.size,-1))
    27972800            self.Indx[valItem.GetId()] = prompt
     
    28102813            btn = wx.Button(self, wx.ID_ANY,'+',style=wx.BU_EXACTFIT)
    28112814            btn.Bind(wx.EVT_BUTTON,self.onExpand)
    2812             btnsizer.Add(btn,0,wx.ALIGN_RIGHT)
     2815            btnsizer.Add(btn)
    28132816        mainSizer.Add(btnsizer,0,wx.EXPAND)
    28142817        self.SetSizer(mainSizer)
     
    59925995        'Get the version number in the dialog'
    59935996        return self.spin.GetValue()
     5997
     5998################################################################################
     5999################################################################################
     6000import wx.lib.mixins.listctrl  as  listmix
     6001class SortableLstCtrl(wx.Panel):
     6002    '''Creates a read-only table with sortable columns. Sorting is done by
     6003    clicking on a column label. A triangle facing up or down is added to
     6004    indicate the column is sorted.
     6005
     6006    To use, the header is labeled using
     6007    :meth:`PopulateHeader`, then :meth:`PopulateLine` is called for every
     6008    row in table and finally :meth:`SetColWidth` is called to set the column
     6009    widths.
     6010
     6011    :param wx.Frame parent: parent object for control
     6012    '''
     6013    def __init__(self, parent):
     6014        wx.Panel.__init__(self, parent, wx.ID_ANY)#, style=wx.WANTS_CHARS)
     6015        sizer = wx.BoxSizer(wx.VERTICAL)
     6016        self.list = G2LstCtrl(self, wx.ID_ANY, style=wx.LC_REPORT| wx.BORDER_SUNKEN)
     6017        sizer.Add(self.list, 1, wx.EXPAND)
     6018        self.SetSizer(sizer)
     6019        self.SetAutoLayout(True)
     6020        #self.SortListItems(0, True)
     6021
     6022    def PopulateHeader(self, header, justify):
     6023        '''Defines the column labels
     6024
     6025        :param list header: a list of strings with header labels
     6026        :param list justify: a list of int values where 0 causes left justification,
     6027          1 causes right justification, and -1 causes centering
     6028        '''
     6029        info = wx.ListItem()
     6030        info.Mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
     6031        info.Image = -1
     6032        for i,(h,j) in enumerate(zip(header, justify)):
     6033            info.Text = h
     6034            if j > 0:
     6035                info.Align =  wx.LIST_FORMAT_RIGHT
     6036            elif j < 0:
     6037                info.Align =  wx.LIST_FORMAT_CENTER
     6038            else:
     6039                info.Align = 0
     6040            self.list.InsertColumn(i, info)
     6041        listmix.ColumnSorterMixin.__init__(self.list, len(header))
     6042        self.list.itemDataMap = {}
     6043
     6044    def PopulateLine(self, key, data):
     6045        '''Enters each row into the table
     6046
     6047        :param int key: a unique int value for each line, probably should
     6048          be sequential
     6049        :param list data: a list of strings for each column in that row
     6050        '''
     6051        index = self.list.InsertItem(self.list.GetItemCount(), data[0])
     6052        for i,d in enumerate(data[1:]):
     6053            self.list.SetItem(index, i+1, d)
     6054        self.list.SetItemData(index, key)
     6055        self.list.itemDataMap[key] = data
     6056
     6057    def SetColWidth(self,col,width=None,auto=True,minwidth=0,maxwidth=None):
     6058        '''Sets the column width.
     6059
     6060        :param int width: the column width in pixels
     6061        :param bool auto: if True (default) and width is None (default) the
     6062          width is set by the maximum width entry in the column
     6063        :param int minwidth: used when auto is True, sets a minimum
     6064          column width
     6065        :param int maxwidth: used when auto is True, sets a maximum
     6066          column width. Do not use with minwidth
     6067        '''
     6068        if width:
     6069            self.list.SetColumnWidth(col, width)
     6070        elif auto:
     6071            self.list.SetColumnWidth(col, wx.LIST_AUTOSIZE)
     6072            if minwidth and self.list.GetColumnWidth(col) < minwidth:
     6073                self.list.SetColumnWidth(col, minwidth)
     6074            elif maxwidth and self.list.GetColumnWidth(col) > maxwidth:
     6075                self.list.SetColumnWidth(col, maxwidth)
     6076        else:
     6077            print('Error in SetColWidth: use either auto or width')
     6078           
     6079class G2LstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ColumnSorterMixin):
     6080    '''Creates a custom ListCtrl with support for images in column labels
     6081    '''
     6082    def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition,
     6083                 size=wx.DefaultSize, style=0):
     6084        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
     6085        listmix.ListCtrlAutoWidthMixin.__init__(self)
     6086        from wx.lib.embeddedimage import PyEmbeddedImage
     6087        # from demo/images.py
     6088        SmallUpArrow = PyEmbeddedImage(
     6089            b"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADxJ"
     6090            b"REFUOI1jZGRiZqAEMFGke2gY8P/f3/9kGwDTjM8QnAaga8JlCG3CAJdt2MQxDCAUaOjyjKMp"
     6091            b"cRAYAABS2CPsss3BWQAAAABJRU5ErkJggg==")
     6092        SmallDnArrow = PyEmbeddedImage(
     6093            b"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAEhJ"
     6094            b"REFUOI1jZGRiZqAEMFGke9QABgYGBgYWdIH///7+J6SJkYmZEacLkCUJacZqAD5DsInTLhDR"
     6095            b"bcPlKrwugGnCFy6Mo3mBAQChDgRlP4RC7wAAAABJRU5ErkJggg==")
     6096        self.il = wx.ImageList(16, 16)
     6097        self.UpArrow = self.il.Add(SmallUpArrow.GetBitmap())
     6098        self.DownArrow = self.il.Add(SmallDnArrow.GetBitmap())
     6099        self.parent=parent
     6100        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
     6101       
     6102    def GetListCtrl(self): # needed for sorting
     6103        return self
     6104    def GetSortImages(self):
     6105        #return (self.parent.DownArrow, self.parent.UpArrow)
     6106        return (self.DownArrow, self.UpArrow)
    59946107
    59956108################################################################################
Note: See TracChangeset for help on using the changeset viewer.