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

Last change on this file since 6341 was 6341, checked in by rwilson, 14 years ago

Rough GUI mapped out.

File size: 14.7 KB
Line 
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
15import pylab
16
17try:
18    import wx
19except ImportError:
20    import tkinter_error
21    msg = ('Sorry, you have to install WxPython.\n'
22           'Get it from either:\n'
23           r'   N:\georisk\downloads\event_selection, or' '\n'
24           '   [http://www.wxpython.org/download.php#binaries].')
25    tkinter_error.tkinter_error(msg)
26    print msg
27    sys.exit(1)
28
29Imported_PyEmbeddedImage = True
30try:
31    from wx.lib.embeddedimage import PyEmbeddedImage
32except ImportError:
33    Imported_PyEmbeddedImage = False
34
35
36# program name and version
37APP_NAME = 'plotcsv'
38APP_VERSION = '0.1'
39
40# name of the configuration filename
41# GUI values are saved in this file for next time
42ConfigFilename = '%s.cfg' % APP_NAME
43
44# GUI definitions
45BUTTON_WIDTH = 100
46BUTTON_HEIGHT = 30
47
48MARGIN = 10
49
50LAB_CTRL_OFFSET = 3
51
52BOX_WIDTH = 400
53BOX_HEIGHT = 360
54BOX_CSV_WIDTH = 400
55BOX_CSV_HEIGHT = 265
56BOX_PLOT_WIDTH = 400
57BOX_PLOT_HEIGHT = 90
58
59TXT_CSVFILE_WIDTH = 390
60TXT_CSVFILE_HEIGHT = 200
61
62COLLAB_X_OFFSET = 20
63
64CHBOX_HEIGHT = 30
65CHBOX_WIDTH = 100
66
67FORM_WIDTH = BOX_WIDTH + 15
68FORM_HEIGHT = 405
69
70START_YOFFSET = 7
71OUT_TEXT_XOFFSET = 8
72OUT_TEXT_YOFFSET = 11
73OUTPUT_BOX_HEIGHT = 45
74
75GEN_YOFFSET = 15
76GEN_DELTAY = 15
77GEN_LABELXOFFSET = 5
78GEN_TEXTXOFFSET = BOX_WIDTH/2 + GEN_LABELXOFFSET + 10
79
80TEXTLIST_WIDTH = BOX_WIDTH - 10
81TEXTLIST_HEIGHT = 200
82
83CTL_WIDTH = BOX_WIDTH / 2
84CTL_HEIGHT = 22
85
86DOUBLE_BUTTON_OFFSET = 8
87
88COMBO_FUDGE = 2
89
90# Copies of various parameters
91Region = ''
92GaugeNumber = ''
93MinimumHeight = ''
94MaximumHeight = ''
95
96# Flag strings - keys in the 'options' dictionary
97X_DATACOL = 'x_datacol'
98Y_DATACOL = 'y_datacol'
99X_RANGE = 'x_range'
100Y_RANGE = 'y_range'
101FILENAME = 'filename'
102SIZE = 'size'
103TITLE = 'title'
104X_LABEL = 'x_label'
105Y_LABEL = 'y_label'
106
107
108################################################################################
109# This code was generated by img2py.py
110# Embed the ICON image here, so we don't need an external *.ico file.
111################################################################################
112
113if Imported_PyEmbeddedImage:
114    def getIcon():
115        return PyEmbeddedImage(
116    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAApxJ"
117    "REFUWIXtlr9O40AQh2edWwsKJFAarGtcRaElJWkoo4TGZegocqLME6TOC6CtaWjCFXRUfgJE"
118    "xStsiivuKiQ4ND8Kx/bu+k+cUKRhpC0ie+f7sjs7a0FEoB3GDyIiYDcOQgjydkI24lvgW2Bj"
119    "gctLolaLaG+P6OiI6PCQSIh89HpEy+VmOdE0lAKI1o8oArSuycMKAQegVQ9qDJeymUCdhGIF"
120    "yRLE1FzAhZ+dATc3wN0d8PiYg9z3XAkLbgl0u4DnAePxWvja5XXen0zK4RFHhoA5lNoabs4z"
121    "Uw4finDN2hAIw/xtKQGltoanMZut5k4U6K0IB2AIaJ0QVrQPT+LaU1vD0xg+2PDuSwJXCggC"
122    "UwAoSLyTxC9SpfAFLxBwAJ99THlavg1uwS0i0LHGcGiurHMKbucav0Uu8V9I/JsrK7FmjRM+"
123    "yRL77CPmuBbefUngxeNqCKR7HpDGPUWFmkjhEUf5v1qNkMNMoqzaNWtMJsU+kQmMx/bDq4HG"
124    "68CW0LdzC97iFtrctiSmPC2Fpzvc7yenvd9PfmcC+/s56/x8tedGTehjQnQvssSSJRQrxBwj"
125    "5LCwIi68KqwV8LykHz0/mxuuoa8GiBZ5YvnhQXFeF2USTeCWQFVo1oheBzn8jaCuPatZpRId"
126    "7sBnHyMeJXCtgaen2vNbK+AWnHwXUJNiYZZP1slylt1Sp6eZVKVAAc4S6u/c6hO1Em7JV1yV"
127    "pQKl8HTPnWZVKpH14PX3dUGgFm4ub5WEewvNZvZc54KxBBrB6ySmU+DgoHgPu2FIZAIbwask"
128    "zNHrrf9oCIxPMrO3N4LXSYQhEMeNpmcCKVywaA43JUYjwPeBTqcx3BIIOYTHHi74YjP4F4OI"
129    "IIgI9JOI2kS0JKI/m33TfzU+ASG0bvT61fpzAAAAAElFTkSuQmCC")
130
131
132##
133# @brief Issue an error message.
134# @param msg The message.
135def error(msg):
136    print >>sys.stderr, msg
137    sys.exit(10)
138
139 
140##
141# @brief Plot a sequence of data.
142# @param x_data The X data sequence to plot.
143# @param y_data A list of one or more Y data sequences to plot.
144# @param options A dictionary of plot options.
145def plot_data(x_data, y_data, options):
146    pylab.plot(x_data, y_data)
147    pylab.title(options.get(TITLE, ''))
148    pylab.grid(True)
149
150    # if user request a particular Y range
151    if not options[Y_RANGE] is None:
152        try:
153            (minimum, maximum) = options[Y_RANGE].split(',')
154            minimum = float(minimum)
155            maximum = float(maximum)
156        except:
157            error('Sorry, got a bad value for Y range: %s' % options[Y_RANGE])
158        pylab.ylim(ymin=minimum, ymax=maximum)
159
160    pylab.xlabel(options.get(X_LABEL, ''))
161    pylab.ylabel(options.get(Y_LABEL, ''))
162
163    pylab.show()
164
165
166##
167# @brief Plot a data file.
168# @param filename Path to the data file to plot.
169# @param options A dictionary of options.
170def plot_file(filename, options=None):
171    # set options defaults
172    opts = options
173    if opts is None:
174        opts = {}
175
176    opts[X_DATACOL] = opts.get(X_DATACOL, '0')
177    opts[Y_DATACOL] = opts.get(Y_DATACOL, '1')
178    opts[SIZE] = opts.get(SIZE, '800,600')
179    opts[Y_RANGE] = opts.get(Y_RANGE, None)
180
181    # get contents of data file
182    # after this, 'header' is list of column header strings
183    #             'data' is a list of lists of data
184    fd = open(filename)
185    c = csv.reader(fd)
186    data = []
187    for row in c:
188        data.append(row)
189    fd.close()
190    header = data[0]
191    del data[0]
192
193    # convert column specifiers to 'int' if required
194    try:
195        index = int(opts[X_DATACOL])
196    except:
197        try:
198            index = header.index(opts[X_DATACOL])
199        except ValueError:
200            error("Sorry, X column header '%s' isn't in the data file." % opts[X_DATACOL])
201    opts[X_DATACOL] = index
202
203    try:
204        index = int(opts[Y_DATACOL])
205    except:
206        try:
207            index = header.index(opts[Y_DATACOL])
208        except ValueError:
209            error("Sorry, Y column header '%s' isn't in the data file." % opts[Y_DATACOL])
210    opts[Y_DATACOL] = index
211   
212    # extract required columns from the data
213    x_col = opts[X_DATACOL]
214    y_col = opts[Y_DATACOL]
215
216    # get max column number, check requested columns
217    max_col = len(header)
218    if x_col >= max_col or y_col >= max_col:
219        error('Sorry, maximum column number for that file is %d.' % (max_col-1))
220
221    x_label = header[x_col].title()
222    x_data = map(lambda x: x[x_col], data)
223    if x_label == 'Time':
224        x_label = 'Time (hours)'
225        x_data = map(lambda x: float(x)/3600., x_data)
226    y_data = map(lambda x: x[y_col], data)
227    y_label = header[y_col].title()
228
229    opts[TITLE] = 'File: %s' % filename
230    opts[X_LABEL] = x_label
231    opts[Y_LABEL] = y_label
232
233    plot_data(x_data, y_data, opts)
234
235
236class MyFrame(wx.Frame):
237    def __init__(self, parent, id, title, pos=wx.DefaultPosition,
238                    size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
239        # Make the frame
240        wx.Frame.__init__(self, parent, id, title, pos=(50, 50),
241                            size=(FORM_WIDTH, FORM_HEIGHT),
242                            style=(wx.DEFAULT_FRAME_STYLE &
243                                   ~ (wx.RESIZE_BOX | wx.MAXIMIZE_BOX |
244                                      wx.RESIZE_BORDER)))
245
246        p = self.panel = wx.Panel(self, -1)
247
248        if Imported_PyEmbeddedImage:
249            tsunami = getIcon()
250            icon = tsunami.GetIcon()
251            self.SetIcon(icon)
252       
253        self.Center()
254        self.Show(True)
255
256        # start laying out controls
257        Y_OFFSET = START_YOFFSET
258        wx.StaticBox(p, -1, 'CSV files', (3, Y_OFFSET),
259                     size=(BOX_CSV_WIDTH, BOX_CSV_HEIGHT))
260
261##        wx.StaticText(p, -1, 'Minimum wave height',
262##                      pos=(GEN_LABELXOFFSET, Y_OFFSET),
263##                      size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT)
264##        self.txtMinH = wx.TextCtrl(p, -1, size=(100, 20),
265##                                   pos=(GEN_TEXTXOFFSET, Y_OFFSET))
266        Y_OFFSET += GEN_DELTAY
267
268##        self.lstGenerate = wx.ListCtrl(p, -1, pos=(8, Y_OFFSET),
269##                                       size=(TEXTLIST_WIDTH,TEXTLIST_HEIGHT),
270##                                       style=wx.LC_SINGLE_SEL)
271##        self.txtCSVFiles = wx.TextCtrl(p, -1,
272##                        "Here is a looooooooooooooong line of text set in the control.\n\n"
273##                        "The quick brown fox jumped over the lazy dog...",
274##                       pos=(8, Y_OFFSET), size=(TXT_CSVFILE_WIDTH,TXT_CSVFILE_HEIGHT), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
275        self.txtCSVFiles = wx.ListBox(p, -1, pos=(8, Y_OFFSET), size=(TXT_CSVFILE_WIDTH,TXT_CSVFILE_HEIGHT))
276        Y_OFFSET += TXT_CSVFILE_HEIGHT + MARGIN
277        x = FORM_WIDTH/2 - BUTTON_WIDTH - DOUBLE_BUTTON_OFFSET
278        self.btnAddCSVFile = wx.Button(p, label="Add", pos=(x, Y_OFFSET),
279                                     size=(100, BUTTON_HEIGHT))
280        x = FORM_WIDTH/2 + DOUBLE_BUTTON_OFFSET
281        self.btnDelCSVFile = wx.Button(p, label="Delete", pos=(x, Y_OFFSET),
282                                     size=(100, BUTTON_HEIGHT))
283        Y_OFFSET += BUTTON_HEIGHT + MARGIN
284
285        wx.StaticBox(p, -1, 'Plot details', (3, Y_OFFSET),
286                     size=(BOX_PLOT_WIDTH, BOX_PLOT_HEIGHT))
287        Y_OFFSET += GEN_DELTAY
288        wx.StaticText(p, -1, 'X-Column',
289                      pos=(COLLAB_X_OFFSET, Y_OFFSET+LAB_CTRL_OFFSET), style=wx.ALIGN_LEFT)
290        self.cbXColHdr = wx.ComboBox(p, -1, pos=(COLLAB_X_OFFSET+50, Y_OFFSET),
291                                     size=(80, -1), style=wx.CB_DROPDOWN)
292        wx.StaticText(p, -1, 'Y-Column',
293                      pos=(FORM_WIDTH/2+COLLAB_X_OFFSET,
294                           Y_OFFSET+LAB_CTRL_OFFSET), style=wx.ALIGN_LEFT)
295        x = FORM_WIDTH/2 + COLLAB_X_OFFSET
296        self.cbYColHdr = wx.ComboBox(p, -1, pos=(x+50, Y_OFFSET),
297                                     size=(80, -1), style=wx.CB_DROPDOWN)
298
299        Y_OFFSET += BUTTON_HEIGHT + MARGIN
300        x = FORM_WIDTH/2 - BUTTON_WIDTH/2
301        self.btnPlote = wx.Button(p, label="Plot", pos=(x, Y_OFFSET),
302                                     size=(100, BUTTON_HEIGHT))
303
304
305
306
307
308##        wx.StaticText(p, -1, 'Maximum wave height',
309##                      pos=(GEN_LABELXOFFSET, Y_OFFSET),
310##                      size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT)
311##        self.chXColHdr = wx.Choice(p, -1, size=(CHBOX_WIDTH, CHBOX_HEIGHT),
312##                                   pos=(10,Y_OFFSET))
313
314
315
316
317
318
319##        wx.StaticText(p, -1, 'Maximum wave height',
320##                      pos=(GEN_LABELXOFFSET, Y_OFFSET),
321##                      size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT)
322##        self.txtMaxH = wx.TextCtrl(p, -1, size=(100, 20),
323##                                   pos=(GEN_TEXTXOFFSET, Y_OFFSET))
324##        Y_OFFSET += GEN_DELTAY
325##        x = FORM_WIDTH/2 - BUTTON_WIDTH/2
326##        self.btnGenerate = wx.Button(p, label="List",
327##                                     pos=(x, Y_OFFSET),
328##                                     size=(100, BUTTON_HEIGHT))
329##        Y_OFFSET += GEN_DELTAY + BUTTON_HEIGHT/2
330##
331##        self.lstGenerate = wx.ListCtrl(p, -1, pos=(8, Y_OFFSET),
332##                                       size=(TEXTLIST_WIDTH,TEXTLIST_HEIGHT),
333##                                       style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
334##       
335##        Y_OFFSET += TEXTLIST_HEIGHT + 20
336##        self.Bind(wx.EVT_BUTTON, self.GenerateClick, self.btnGenerate)
337##        self.lstGenerate.Bind(wx.EVT_LIST_ITEM_SELECTED,
338##                              self.OnItemSelected, self.lstGenerate)
339##
340##        # start laying out get_multimux controls
341##        wx.StaticBox(p, -1, 'multimux_grid', (3, Y_OFFSET),
342##                     size=(BOX_WIDTH, BOX2_HEIGHT))
343##        Y_OFFSET += 18
344##        wx.StaticText(p, -1, 'Quake ID', pos=(GEN_LABELXOFFSET, Y_OFFSET),
345##                      size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT)
346##        self.txtQuakeID = wx.TextCtrl(p, -1, size=(100, 20),
347##                                      pos=(GEN_TEXTXOFFSET, Y_OFFSET))
348##        Y_OFFSET += GEN_DELTAY
349##        x = FORM_WIDTH/2 - BUTTON_WIDTH - DOUBLE_BUTTON_OFFSET
350##        self.btnMultimux = wx.Button(p, label="Multimux", pos=(x, Y_OFFSET),
351##                                     size=(100, BUTTON_HEIGHT))
352##        x = FORM_WIDTH/2 + DOUBLE_BUTTON_OFFSET
353##        self.btnGrid = wx.Button(p, label="Grid", pos=(x, Y_OFFSET),
354##                                     size=(100, BUTTON_HEIGHT))
355##        Y_OFFSET += GEN_DELTAY + BUTTON_HEIGHT/2
356##        self.lstMultimux = wx.ListCtrl(p, -1, pos=(8, Y_OFFSET),
357##                                       size=(TEXTLIST_WIDTH,TEXTLIST_HEIGHT),
358##                                       style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
359##        Y_OFFSET += TEXTLIST_HEIGHT + 20
360##        self.Bind(wx.EVT_BUTTON, self.MultimuxClick, self.btnMultimux)
361##        self.Bind(wx.EVT_BUTTON, self.GridClick, self.btnGrid)
362
363        self.Bind(wx.EVT_CLOSE, self.doStateSave)
364        self.doStateRestore()
365
366    def doStateSave(self, event):
367        """
368        Save state here.
369        """
370##        self.cfg['OutputDirectory'] = self.txtOutputDir.GetValue()
371##        self.cfg['Region'] = self.cbRegion.GetValue()
372##        self.cfg['GaugeNumber'] = self.txtTGN.GetValue()
373##        self.cfg['MinimumHeight'] = self.txtMinH.GetValue()
374##        self.cfg['MaximumHeight'] = self.txtMaxH.GetValue()
375##        self.cfg.save()
376       
377        event.Skip(True)
378
379    def doStateRestore(self):
380        """
381        Restore state here - globals have been set and tested sane
382        """
383##        global Region, GaugeNumber, MinimumHeight, MaximumHeight
384##
385##        cfg = Config(ConfigFilename)
386##        output_dir = cfg['OutputDirectory']
387##        Region = cfg['Region']
388##        GaugeNumber = cfg['GaugeNumber']
389##        if GaugeNumber:
390##            GaugeNumber = int(GaugeNumber)
391##        MinimumHeight = cfg['MinimumHeight']
392##        if MinimumHeight:
393##            MinimumHeight = float(MinimumHeight)
394##        MaximumHeight = cfg['MaximumHeight']
395##        if MaximumHeight:
396##            MaximumHeight = float(MaximumHeight)
397##
398##        self.cfg = cfg
399##
400##        region_name = None
401##        for (name, dir) in Regions:
402##            if name == Region:
403##                region_name = name
404##        if not region_name:
405##            Region = ''
406##
407##        if output_dir:
408##            self.txtOutputDir.WriteText(output_dir)
409##        self.cbRegion.SetStringSelection(Region)
410##        if GaugeNumber:
411##            self.txtTGN.WriteText(str(GaugeNumber))
412##        if MinimumHeight:
413##            self.txtMinH.WriteText(str(MinimumHeight))
414##        if MaximumHeight:
415##            self.txtMaxH.WriteText(str(MaximumHeight))
416       
417    def error(self, msg):
418        dlg = wx.MessageDialog(self, msg, 'Error',
419                               wx.OK | wx.ICON_INFORMATION)
420        dlg.ShowModal()
421        dlg.Destroy()
422
423       
424################################################################################
425# Code to do the GUI
426################################################################################
427
428if __name__ == '__main__':
429    app = wx.App()
430    frame = MyFrame(None, -1, '%s %s' % (APP_NAME, APP_VERSION),
431                    size=(FORM_WIDTH, FORM_HEIGHT))
432    app.SetTopWindow(frame)
433    app.MainLoop()
434
Note: See TracBrowser for help on using the repository browser.