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