source: misc/tools/plotcsv/plotcsv.py @ 6480

Last change on this file since 6480 was 6480, checked in by rwilson, 15 years ago

Fixed clumsy code, bumped verion # for next release.

File size: 20.6 KB
RevLine 
[6338]1#!/usr/bin/env python
2
3'''A module to draw a graph on the screen given a data file.
4
5The code is written such that it may be run as a program or imported
6and used as a module.
7'''
8
9import sys
10import os.path
11import types
12import csv
13import time
14import getopt
[6480]15import string
[6344]16try:
17    import cpickle
18except:
19    import pickle
[6338]20
[6341]21try:
[6356]22    import pylab
23except ImportError:
24    import tkinter_error
25    msg = ('Sorry, you have to install matplotlib.\n'
26           'Get it from either:\n'
27           r'   N:\georisk\downloads\matplotlib, or' '\n'
28           '   [http://matplotlib.sourceforge.net/].\n\n'
29           'You will also need to have numpy installed:\n'
30           r'   N:\georisk\downloads\numpy')
31    tkinter_error.tkinter_error(msg)
32    print msg
33    sys.exit(1)
34
35try:
[6341]36    import wx
37except ImportError:
38    import tkinter_error
39    msg = ('Sorry, you have to install WxPython.\n'
40           'Get it from either:\n'
41           r'   N:\georisk\downloads\event_selection, or' '\n'
42           '   [http://www.wxpython.org/download.php#binaries].')
43    tkinter_error.tkinter_error(msg)
44    print msg
45    sys.exit(1)
[6338]46
[6341]47Imported_PyEmbeddedImage = True
48try:
49    from wx.lib.embeddedimage import PyEmbeddedImage
50except ImportError:
51    Imported_PyEmbeddedImage = False
52
53
54# program name and version
55APP_NAME = 'plotcsv'
[6480]56APP_VERSION = '0.4'
[6341]57
58# name of the configuration filename
59# GUI values are saved in this file for next time
60ConfigFilename = '%s.cfg' % APP_NAME
61
62# GUI definitions
63BUTTON_WIDTH = 100
64BUTTON_HEIGHT = 30
65
66MARGIN = 10
67
[6348]68LAB_CTRL_OFFSET = 5
[6341]69
70BOX_WIDTH = 400
71BOX_HEIGHT = 360
72BOX_CSV_WIDTH = 400
73BOX_CSV_HEIGHT = 265
74BOX_PLOT_WIDTH = 400
[6356]75BOX_PLOT_HEIGHT = 140
[6341]76
77TXT_CSVFILE_WIDTH = 390
78TXT_CSVFILE_HEIGHT = 200
79
[6348]80COLLAB_X_OFFSET = 25
[6341]81
82CHBOX_HEIGHT = 30
83CHBOX_WIDTH = 100
84
85FORM_WIDTH = BOX_WIDTH + 15
[6358]86FORM_HEIGHT = 450
[6341]87
88START_YOFFSET = 7
89OUT_TEXT_XOFFSET = 8
90OUT_TEXT_YOFFSET = 11
91OUTPUT_BOX_HEIGHT = 45
92
93GEN_YOFFSET = 15
94GEN_DELTAY = 15
95GEN_LABELXOFFSET = 5
96GEN_TEXTXOFFSET = BOX_WIDTH/2 + GEN_LABELXOFFSET + 10
97
98TEXTLIST_WIDTH = BOX_WIDTH - 10
99TEXTLIST_HEIGHT = 200
100
101CTL_WIDTH = BOX_WIDTH / 2
102CTL_HEIGHT = 22
103
104DOUBLE_BUTTON_OFFSET = 8
105
106COMBO_FUDGE = 2
107
108# Copies of various parameters
109Region = ''
110GaugeNumber = ''
111MinimumHeight = ''
112MaximumHeight = ''
113
[6338]114# Flag strings - keys in the 'options' dictionary
115X_DATACOL = 'x_datacol'
116Y_DATACOL = 'y_datacol'
117X_RANGE = 'x_range'
118Y_RANGE = 'y_range'
119FILENAME = 'filename'
120SIZE = 'size'
121TITLE = 'title'
122X_LABEL = 'x_label'
123Y_LABEL = 'y_label'
124
[6344]125CSVWildcard = 'CSV files (*.csv)|*.csv|All files (*.*)|*.*'
[6338]126
[6344]127# Flag strings - keys in the 'options' dictionary
128X_DATACOL = 'x_datacol'
129Y_DATACOL = 'y_datacol'
130X_RANGE = 'x_range'
131Y_RANGE = 'y_range'
132FILENAME = 'filename'
133SIZE = 'size'
134TITLE = 'title'
135X_LABEL = 'x_label'
136Y_LABEL = 'y_label'
137
138
[6341]139################################################################################
140# This code was generated by img2py.py
141# Embed the ICON image here, so we don't need an external *.ico file.
142################################################################################
143
144if Imported_PyEmbeddedImage:
145    def getIcon():
146        return PyEmbeddedImage(
147    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAApxJ"
148    "REFUWIXtlr9O40AQh2edWwsKJFAarGtcRaElJWkoo4TGZegocqLME6TOC6CtaWjCFXRUfgJE"
149    "xStsiivuKiQ4ND8Kx/bu+k+cUKRhpC0ie+f7sjs7a0FEoB3GDyIiYDcOQgjydkI24lvgW2Bj"
150    "gctLolaLaG+P6OiI6PCQSIh89HpEy+VmOdE0lAKI1o8oArSuycMKAQegVQ9qDJeymUCdhGIF"
151    "yRLE1FzAhZ+dATc3wN0d8PiYg9z3XAkLbgl0u4DnAePxWvja5XXen0zK4RFHhoA5lNoabs4z"
152    "Uw4finDN2hAIw/xtKQGltoanMZut5k4U6K0IB2AIaJ0QVrQPT+LaU1vD0xg+2PDuSwJXCggC"
153    "UwAoSLyTxC9SpfAFLxBwAJ99THlavg1uwS0i0LHGcGiurHMKbucav0Uu8V9I/JsrK7FmjRM+"
154    "yRL77CPmuBbefUngxeNqCKR7HpDGPUWFmkjhEUf5v1qNkMNMoqzaNWtMJsU+kQmMx/bDq4HG"
155    "68CW0LdzC97iFtrctiSmPC2Fpzvc7yenvd9PfmcC+/s56/x8tedGTehjQnQvssSSJRQrxBwj"
156    "5LCwIi68KqwV8LykHz0/mxuuoa8GiBZ5YvnhQXFeF2USTeCWQFVo1oheBzn8jaCuPatZpRId"
157    "7sBnHyMeJXCtgaen2vNbK+AWnHwXUJNiYZZP1slylt1Sp6eZVKVAAc4S6u/c6hO1Em7JV1yV"
158    "pQKl8HTPnWZVKpH14PX3dUGgFm4ub5WEewvNZvZc54KxBBrB6ySmU+DgoHgPu2FIZAIbwask"
159    "zNHrrf9oCIxPMrO3N4LXSYQhEMeNpmcCKVywaA43JUYjwPeBTqcx3BIIOYTHHi74YjP4F4OI"
160    "IIgI9JOI2kS0JKI/m33TfzU+ASG0bvT61fpzAAAAAElFTkSuQmCC")
161
162
[6348]163def error(msg):
164    print msg
[6477]165    import tkinter_error
166    tkinter_error.tkinter_error(msg)
[6348]167    sys.exit(10)
168
169
[6344]170################################################################################
171# An object that behaves like a dictionary but is pickled/unpickled from a file.
[6477]172# Used to save state in the application.
[6344]173################################################################################
[6338]174
[6344]175class Config(object):
[6350]176    """An object that behaves like a dictionary but is persistent:
[6344]177
178        cfg = Config('filename')
179        cfg['save'] = 'A saved value'
180        cfg[123] = 'value 123'
181        print cfg['save']
182
183    The values stored in the file 'filename' will be available to any
184    application using that config file.
185    """
186
187    def __init__(self, configfile=None):
[6350]188        """__init__(self, String filename=None) -> Config"""
[6344]189
190        self.delconf = False
191        self.cfgdict = {}
192        self.changed = False
193        if not configfile:
194            self.delconf = True
195            configfile =tempfile.mktemp()
196        self.configfile = os.path.abspath(configfile)
197        if os.path.exists(self.configfile):
198            try:
199                f = open(configfile, "r")
200                u = pickle.Unpickler(f)
201                self.cfgdict = u.load()
202            except pickle.UnpicklingError, e:
203                print e
204            except:
205                pass
206            else:
207                f.close()
208
209    def __setitem__(self, key, value):
[6350]210        """Override to allow: cfg[<key>] = <value>"""
211       
[6344]212        self.cfgdict[key] = value
213        self.changed = True
214
215    def __getitem__(self, key):
[6350]216        """Override to allow: <var> = cfg[<key>]"""
217       
[6344]218        return self.cfgdict.get(key, None)
219
220    def __str__(self):
[6350]221        """__str__(self) -> String"""
222       
[6344]223        return "<config object at %s>" % hex(id(self))
224
225    def getfilename(self):
[6350]226        """getfilename(self) -> String filename"""
227       
[6344]228        return self.configfile
229
230    def setdeleted(self):
[6350]231        """setdeleted(self)"""
232       
[6344]233        self.delconf = True
234
235    def save(self):
[6350]236        """save(self)"""
237       
[6344]238        try:
239            f = open(self.configfile, "w")
240            p = pickle.Pickler(f)
241            p.dump(self.cfgdict)
242        except pickle.PicklingError, e:
243            print e
244        else:
245            f.close()
246        self.changed = False
247
248    def close(self):
[6350]249        """close(self)"""
250       
[6344]251        if self.changed:
252            self.save()
253        if self.delconf:
254            if os.path.exists(self.configfile):
255                os.remove(self.configfile)
256
257    def __del__(self):
[6350]258        """__del__(self)"""
259       
[6344]260        self.close()
[6350]261       
[6344]262
263################################################################################
264# Plot routines
265################################################################################
266
[6338]267##
[6344]268# @brief Get CSV data from a file.
[6338]269# @param filename Path to the data file to plot.
[6348]270# @param x_hdr The X axis title string.
271# @param y_hdr The Y axis title string.
272def getCSVData(filename, x_hdr, y_hdr):
[6338]273    # get contents of data file
274    # after this, 'header' is list of column header strings
275    #             'data' is a list of lists of data
276    fd = open(filename)
277    c = csv.reader(fd)
278    data = []
279    for row in c:
280        data.append(row)
281    fd.close()
282    header = data[0]
[6344]283    del data[0]         # get rid of header in dataset
[6338]284
[6477]285    # ensure header strings don't have leading/trailing spaces
[6480]286    header = map(string.strip, header)
[6477]287
[6348]288    # get int index values for column headers
[6338]289    try:
[6348]290        x_index = header.index(x_hdr)
291    except ValueError:
[6477]292        error("Sorry, X column header '%s' isn't in data file '%s'."
293              % (x_hdr, filename))
[6338]294
295    try:
[6348]296        y_index = header.index(y_hdr)
297    except ValueError:
[6477]298        error("Sorry, Y column header '%s' isn't in data file '%s'."
299              % (y_hdr, filename))
[6338]300
[6348]301    # get appropriate columns from data[]
[6352]302    x_data = map(lambda x: float(x[x_index]), data)
[6348]303    if x_hdr == 'time':
[6338]304        x_data = map(lambda x: float(x)/3600., x_data)
[6352]305    y_data = map(lambda x: float(x[y_index]), data)
[6338]306
[6344]307    return (x_data, y_data)
[6338]308
309
[6344]310##
311# @brief Plot data files.
312# @param filenames List of full pathnames to plot.
[6348]313# @param x_hdr The X axis label string.
314# @param y_hdr The Y axis label string.
[6358]315# @param title The string used to title the graph.
316# @param legend True if a legend is to be displayed.
317# @param legend_path True if legend is to contain full file paths.
318def plot_files(filenames, x_hdr, y_hdr, title='', legend=False,
319               legend_path=False):
[6348]320    pylab.rc('axes', linewidth=2)
321    pylab.xlabel(x_hdr.title())
322    pylab.ylabel(y_hdr.title())
323    pylab.grid(True)
[6344]324
325    for f in filenames:
[6348]326        (x_data, y_data) = getCSVData(f, x_hdr, y_hdr)
[6344]327        pylab.plot(x_data, y_data)
328
[6356]329    if title:
330        pylab.title(title)
331    if legend:
332        (min_y, max_y) = pylab.ylim()
333        range_y = max_y - min_y
334        add_y = float(range_y) / 5
335        pylab.ylim((min_y, max_y+add_y))
[6358]336        legend_names = filenames
337        if not legend_path:
338            legend_names = [os.path.basename(x) for x in filenames]
339        pylab.legend(legend_names, 'upper right')
[6348]340
[6344]341    pylab.show()
[6477]342    pylab.close()
[6344]343
344
[6351]345################################################################################
346# GUI routines
347################################################################################
348
[6341]349class MyFrame(wx.Frame):
350    def __init__(self, parent, id, title, pos=wx.DefaultPosition,
351                    size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
[6351]352        '''Lay out the GUI form'''
353       
[6341]354        # Make the frame
355        wx.Frame.__init__(self, parent, id, title, pos=(50, 50),
356                            size=(FORM_WIDTH, FORM_HEIGHT),
357                            style=(wx.DEFAULT_FRAME_STYLE &
358                                   ~ (wx.RESIZE_BOX | wx.MAXIMIZE_BOX |
359                                      wx.RESIZE_BORDER)))
360
361        p = self.panel = wx.Panel(self, -1)
362
363        if Imported_PyEmbeddedImage:
364            tsunami = getIcon()
365            icon = tsunami.GetIcon()
366            self.SetIcon(icon)
367       
368        self.Center()
369        self.Show(True)
370
371        # start laying out controls
372        Y_OFFSET = START_YOFFSET
373        wx.StaticBox(p, -1, 'CSV files', (3, Y_OFFSET),
374                     size=(BOX_CSV_WIDTH, BOX_CSV_HEIGHT))
375        Y_OFFSET += GEN_DELTAY
[6344]376        self.txtCSVFiles = wx.ListBox(p, -1, pos=(8, Y_OFFSET),
377                                      size=(TXT_CSVFILE_WIDTH,
378                                            TXT_CSVFILE_HEIGHT))
379        self.CSVFiles = []
[6341]380        Y_OFFSET += TXT_CSVFILE_HEIGHT + MARGIN
381        x = FORM_WIDTH/2 - BUTTON_WIDTH - DOUBLE_BUTTON_OFFSET
382        self.btnAddCSVFile = wx.Button(p, label="Add", pos=(x, Y_OFFSET),
383                                     size=(100, BUTTON_HEIGHT))
384        x = FORM_WIDTH/2 + DOUBLE_BUTTON_OFFSET
385        self.btnDelCSVFile = wx.Button(p, label="Delete", pos=(x, Y_OFFSET),
386                                     size=(100, BUTTON_HEIGHT))
387        Y_OFFSET += BUTTON_HEIGHT + MARGIN
388
[6348]389        wx.StaticBox(p, -1, 'Plot files', (3, Y_OFFSET),
[6341]390                     size=(BOX_PLOT_WIDTH, BOX_PLOT_HEIGHT))
391        Y_OFFSET += GEN_DELTAY
392        wx.StaticText(p, -1, 'X-Column',
393                      pos=(COLLAB_X_OFFSET, Y_OFFSET+LAB_CTRL_OFFSET), style=wx.ALIGN_LEFT)
[6348]394        self.cbXColHdr = wx.Choice(p, -1, pos=(COLLAB_X_OFFSET+65, Y_OFFSET),
[6344]395                                   size=(80, -1))
396        self.XColHdr = []
[6341]397        wx.StaticText(p, -1, 'Y-Column',
398                      pos=(FORM_WIDTH/2+COLLAB_X_OFFSET,
399                           Y_OFFSET+LAB_CTRL_OFFSET), style=wx.ALIGN_LEFT)
400        x = FORM_WIDTH/2 + COLLAB_X_OFFSET
[6348]401        self.cbYColHdr = wx.Choice(p, -1, pos=(x+65, Y_OFFSET), size=(80, -1))
[6344]402        self.YColHdr = []
[6356]403        Y_OFFSET += GEN_DELTAY*2
[6341]404
[6356]405        self.chkLegend = wx.CheckBox(p, -1, " Show graph legend",
[6358]406                                     pos=(COLLAB_X_OFFSET+20, Y_OFFSET))       
407        self.chkLegendPath = wx.CheckBox(p, -1, " Full file path in legend",
408                                         pos=(COLLAB_X_OFFSET+180, Y_OFFSET))       
409        Y_OFFSET += GEN_DELTAY + 3
[6356]410       
411        wx.StaticText(p, -1, 'Title',
412                      pos=(18, Y_OFFSET+LAB_CTRL_OFFSET+2), style=wx.ALIGN_LEFT)
413        self.txtTitle = wx.TextCtrl(p, -1, "", size=(350, -1),
414                                    pos=(COLLAB_X_OFFSET+20,
415                                         Y_OFFSET+LAB_CTRL_OFFSET),
416                                    style=wx.ALIGN_LEFT)
417       
[6341]418        Y_OFFSET += BUTTON_HEIGHT + MARGIN
419        x = FORM_WIDTH/2 - BUTTON_WIDTH/2
[6344]420        self.btnPlot = wx.Button(p, label="Plot", pos=(x, Y_OFFSET),
[6341]421                                     size=(100, BUTTON_HEIGHT))
422
[6351]423        # bind controls/events to handlers
[6344]424        self.Bind(wx.EVT_BUTTON, self.AddCSVFile, self.btnAddCSVFile)
425        self.Bind(wx.EVT_BUTTON, self.DelCSVFile, self.btnDelCSVFile)
426        self.Bind(wx.EVT_BUTTON, self.PlotFiles, self.btnPlot)
[6358]427        self.Bind(wx.EVT_CHECKBOX, self.ChangeLegend, self.chkLegend)
[6351]428       
[6344]429        self.Bind(wx.EVT_CLOSE, self.doStateSave)
[6351]430
431        # restore saved values
432        self.common_headers = None
[6344]433        self.cfg = Config(ConfigFilename)
434        self.doStateRestore()
435        self.updateHdrChoices()
436       
437    def AddCSVFile(self, event):
438        """Add a CSV file to the listbox"""
[6341]439
[6344]440        dlg = wx.FileDialog(self, message='Choose a CSV file',
441                            defaultDir=self.file_dir, 
442                            defaultFile='',
443                            wildcard=CSVWildcard,
444                            style=wx.OPEN | wx.CHANGE_DIR)
[6341]445
[6344]446        if dlg.ShowModal() == wx.ID_OK:
447            # This returns a list of  files that were selected.
448            path = dlg.GetPath()
449            self.file_dir = dlg.GetDirectory()
450            dlg.Destroy()
451            if path not in self.CSVFiles:
452                headers = self.getHeaders(path)
453                if headers:
454                    common_headers = self.orHeaders(headers)
455                    if not common_headers or len(common_headers) < 2:
456                        self.error("Sorry, file '%s' doesn't have enough headers in common with current files" % path)
457                    else:
458                        self.headers.append(headers)
459                        self.common_headers = common_headers
460                        self.CSVFiles.append(path)
461                        self.txtCSVFiles.Append(path)
462                else:
463                    self.error("Sorry, file '%s' doesn't appear to be a CSV file" % path)
464                self.updateHdrChoices()
[6341]465
[6344]466    def DelCSVFile(self, event):
[6351]467        """Delete a CSV file from the listbox"""
[6341]468
[6344]469        sel = self.txtCSVFiles.GetSelections()
470        if sel:
471            sel = sel[0]
472            self.txtCSVFiles.Delete(sel)
473            del self.CSVFiles[sel]
474            del self.headers[sel]
475            self.common_headers = self.GenCommonHeaders(self.headers)
476            self.updateHdrChoices()
[6341]477
[6358]478    def ChangeLegend(self, event):
479        if self.chkLegend.GetValue():
480            self.chkLegendPath.Enable()
481        else:
482            self.chkLegendPath.Disable()
483
[6344]484    def doStateSave(self, event):
[6351]485        """Save state from 'self' variables & controls."""
[6341]486
[6344]487        self.cfg['CSVFiles'] = self.CSVFiles
488        self.cfg['XColHdr'] = self.XColHdr
489        self.cfg['XColHdrSelection'] = self.cbXColHdr.GetSelection()
490        self.cfg['YColHdr'] = self.YColHdr
491        self.cfg['YColHdrSelection'] = self.cbYColHdr.GetSelection()
492        self.cfg['file_dir'] = self.file_dir
[6356]493        self.cfg['GraphTitle'] = self.txtTitle.GetValue()
494        self.cfg['GraphLegend'] = self.chkLegend.GetValue()
[6358]495        self.cfg['LegendPath'] = self.chkLegendPath.GetValue()
[6341]496
[6344]497        self.cfg.save()
498        event.Skip(True)
[6341]499
[6344]500    def doStateRestore(self):
501        """Restore state to 'self' variables."""
[6341]502
[6344]503        self.CSVFiles = self.cfg['CSVFiles']
504        if self.CSVFiles is None:
505            self.CSVFiles = []
506        self.XColHdr = self.cfg['XColHdr']
507        if self.XColHdr is None:
508            self.XColHdr = []
[6348]509        self.XColHdrSelection = self.cfg['XColHdrSelection']
[6344]510        self.YColHdr = self.cfg['YColHdr']
511        if self.YColHdr is None:
512            self.YColHdr = []
[6348]513        self.YColHdrSelection = self.cfg['YColHdrSelection']
[6344]514        self.file_dir = self.cfg['file_dir']
515        if self.file_dir is None:
516            self.file_dir = os.getcwd()
[6341]517
[6344]518        # put data into controls
519        self.headers = []
520        for (i, f) in enumerate(self.CSVFiles):
521            headers = self.getHeaders(f)
522            self.headers.append(headers)
523            self.txtCSVFiles.Insert(f, i)
[6348]524        self.common_headers = self.GenCommonHeaders(self.headers)
525        self.updateHdrChoices()
[6338]526
[6352]527        if self.XColHdrSelection >= 0:
[6348]528            self.cbXColHdr.SetSelection(self.XColHdrSelection)
[6352]529        if self.YColHdrSelection >= 0:
[6348]530            self.cbYColHdr.SetSelection(self.YColHdrSelection)
[6356]531        title = self.cfg['GraphTitle']
532        if title is None:
533            title = ''
534        self.txtTitle.SetValue(title)
535        if self.cfg['GraphLegend']:
536            self.chkLegend.SetValue(True)
[6358]537        if self.cfg['LegendPath']:
538            self.chkLegendPath.SetValue(True)
539        self.ChangeLegend(None)
540       
[6344]541    def getHeaders(self, filename):
542        '''Get header list from a file'''
[6338]543
[6344]544        try:
545            fd = open(filename, 'r')
546            hdr = fd.readline()
547            fd.close()
548        except:
549            return None
550
551        hdr = hdr.split(',')
552        hdr = [x.strip() for x in hdr]
553
554        return hdr
[6341]555       
[6344]556    def updateHdrChoices(self):
557        '''Update choice controls with header lists.
[6338]558
[6351]559        Disable controls if no CSV files (ie, no headers).
560        Keep selections visible afterwards, if possible.
[6344]561        '''
562
[6351]563        # get current selections, if any
[6344]564        selected_x = self.cbXColHdr.GetStringSelection()
565        selected_y = self.cbYColHdr.GetStringSelection()
[6341]566       
[6344]567        self.cbXColHdr.Clear()
568        self.cbYColHdr.Clear()
569
570        if self.common_headers:
571            self.cbXColHdr.Enable()
572            self.cbYColHdr.Enable()
573            self.btnPlot.Enable()
574            for h in self.common_headers:
575                self.cbXColHdr.Append(h)
576                self.cbYColHdr.Append(h)
577            if selected_x in self.common_headers:
578                index = self.common_headers.index(selected_x)
579                self.cbXColHdr.SetSelection(index)
580            if selected_y in self.common_headers:
581                index = self.common_headers.index(selected_y)
582                self.cbYColHdr.SetSelection(index)
583        else:
584            self.cbXColHdr.Disable()
585            self.cbYColHdr.Disable()
586            self.btnPlot.Disable()
587
588    def orHeaders(self, new_header):
[6351]589        '''Update X & Y column header choices.
[6344]590
[6351]591        Return new 'common headers' list from current
592        self.common_headers and the supplied new_header list.
593        '''
594
[6344]595        if self.common_headers:
596            result = [x for x in new_header if x in self.common_headers]
597        else:
598            result = new_header
599        return result
600
601    def GenCommonHeaders(self, headers):
[6351]602        '''Get new set of common headers.
[6344]603
[6351]604        Return a new 'common header' list given a
605        list of header lists.
606        '''
607
[6344]608        result = []
609        for header in headers:
610            if result:
611                result = [x for x in result if x in header]
612            else:
613                result = header
614        return result
615
616    def PlotFiles(self, event):
[6351]617        '''Plot files in the CSV file listbox.'''
618       
[6344]619        selected_x = self.cbXColHdr.GetStringSelection()
620        selected_y = self.cbYColHdr.GetStringSelection()
621
622        if selected_x and selected_y:
[6356]623            plot_files(self.CSVFiles, selected_x, selected_y,
624                       title=self.txtTitle.GetValue(),
[6358]625                       legend=self.chkLegend.GetValue(),
626                       legend_path=self.chkLegendPath.GetValue()) 
[6344]627           
[6341]628    def error(self, msg):
[6351]629        '''Issue xwPython error message.'''
630       
[6341]631        dlg = wx.MessageDialog(self, msg, 'Error',
632                               wx.OK | wx.ICON_INFORMATION)
633        dlg.ShowModal()
634        dlg.Destroy()
[6338]635
[6341]636       
637################################################################################
[6351]638# Mainline code - start the application.
[6341]639################################################################################
[6338]640
[6341]641if __name__ == '__main__':
642    app = wx.App()
643    frame = MyFrame(None, -1, '%s %s' % (APP_NAME, APP_VERSION),
644                    size=(FORM_WIDTH, FORM_HEIGHT))
645    app.SetTopWindow(frame)
646    app.MainLoop()
[6338]647
Note: See TracBrowser for help on using the repository browser.