4 # PlotFile2 -- A tool to make quick plots from data series.
5 # Version: 0.1-alpha (see README)
7 # Copyright (C) 2007 Antonio Ingargiola <tritemio@gmail.com>
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
15 rootdir
= sys
.path
[0] + '/'
16 sourcedir
= rootdir
+ 'plotfile/'
20 from matplotlib
.lines
import Line2D
21 from common
.gtk_generic_windows
import \
22 GenericMainPlotWindow
, DialogWindowWithCancel
,\
23 GenericOpenFileDialog
, GenericSecondaryWindow
24 from common
.filedata
import xyLabelFromFile
25 from numpy
import arange
, array
, min
27 # XML GUI Description file
28 gladefile
= sourcedir
+ 'plotfile2.glade'
30 # Minimum default axis value in log scale when the original value was <= 0
33 # How many centimeter is an inch
37 """An object describing data series with a shared X axis."""
38 def __init__(self
, x
=None, y1
=None, name
=None):
39 # self.x is the X axis shared for all y series
44 # self.y is a list of series of data
46 if y1
!= None: self
.add(y1
)
48 # self.yline is a list of plotted lines from the current DataSet
51 # Set the plot style, maker and linestyle attributes
52 # Marker and linestyle are separate variables so we can switch them off
53 # and then recover the style information when we turn them on
57 ##self.plot_kwargs = dict(
58 ## linestyle = self.linestyle,
60 ## marker = self.marker,
66 if len(yi
) != self
.len:
67 raise IndexError, 'Y data length (%d) must be == len(x) (%d)'\
73 """Simple struct to store any properties that has different values for X
81 class PlotFileApp(GenericMainPlotWindow
):
83 This class implements an interactive plot windows with various options.
85 def __init__(self
, x
=None, y
=None, title
='Title', debug
=False):
86 GenericMainPlotWindow
.__init
__(self
, 'PlotFileWindow', gladefile
)
93 self
.setup_gui_widgets()
95 # Data: self.data is a list of DataSet() instances
97 self
.data
.append( DataSet(x
, y
, 'Plot 1') )
101 # Try to load the file passed as parameter
102 if x
== None and y
== None:
104 self
.load(sys
.argv
[1])
109 def set_defaults(self
):
115 self
.xlabel
= 'X Axis'
116 self
.ylabel
= 'Y Axis'
117 self
.xscale
= 'linear'
118 self
.yscale
= 'linear'
119 self
.showPoints
= True
120 self
.showLines
= True
124 self
.xmin
, self
.xmax
, self
.ymin
, self
.ymax
= None, None, None, None
125 self
.axes_limits
= AxesProperty()
126 self
.axes_limits
.x
= dict(linear
=None, log
=None)
127 self
.axes_limits
.y
= dict(linear
=None, log
=None)
129 def reset_scale_buttons(self
):
131 if self
.xscale
== 'log':
132 self
.xscaleCheckB
.set_active(True)
134 self
.xscaleCheckB
.set_active(False)
135 if self
.yscale
== 'log':
136 self
.yscaleCheckB
.set_active(True)
138 self
.yscaleCheckB
.set_active(False)
141 def setup_gui_widgets(self
):
142 # Create the text entry handler
143 self
.xminEntry
= self
.widgetTree
.get_widget('xmin_entry')
144 self
.xmaxEntry
= self
.widgetTree
.get_widget('xmax_entry')
145 self
.yminEntry
= self
.widgetTree
.get_widget('ymin_entry')
146 self
.ymaxEntry
= self
.widgetTree
.get_widget('ymax_entry')
148 # Initialize the check buttons to the correct value
149 self
.pointsCheckB
= self
.widgetTree
.get_widget('points_chk_butt')
150 self
.linesCheckB
= self
.widgetTree
.get_widget('lines_chk_butt')
151 self
.xscaleCheckB
= self
.widgetTree
.get_widget('xlog_chk_butt')
152 self
.yscaleCheckB
= self
.widgetTree
.get_widget('ylog_chk_butt')
153 self
.reset_scale_buttons()
155 self
.cooPickerCheckB
= self
.widgetTree
.get_widget('coordinatePicker')
156 self
.moreGridCheckB
= self
.widgetTree
.get_widget('moreGrid')
158 self
.window
.show_all()
159 if self
.debug
: print 'end of setup_gui_widgets()'
161 def is_ydata_positive(self
):
164 if (yi
<= 0).any(): return False
167 def plot_data(self
, n
=0):
168 """Plot the n-th data from the self.data list of DataSet()."""
170 if self
.debug
: print 'plot_data method'
173 d
.yline
= range(len(d
.y
))
174 for i
, yi
in enumerate(d
.y
):
175 d
.yline
[i
], = self
.axis
.plot(d
.x
, yi
, ls
=d
.linestyle
,
178 self
.axis
.set_title(self
.title
)
179 self
.axis
.set_xlabel(self
.xlabel
)
180 self
.axis
.set_ylabel(self
.ylabel
)
186 #self.update_axis_limits_entry()
188 def update_axis_limits_entry(self
):
190 xmin
, xmax
= self
.axis
.get_xlim()
191 self
.xminEntry
.set_text(str(xmin
))
192 self
.xmaxEntry
.set_text(str(xmax
))
194 ymin
, ymax
= self
.axis
.get_ylim()
195 self
.yminEntry
.set_text(str(ymin
))
196 self
.ymaxEntry
.set_text(str(ymax
))
201 if self
.debug
: print 'plot method'
203 if self
.yscale
== 'log' and not self
.is_ydata_positive():
204 self
.writeStatusBar('WARNING: Negative Y values in log scale.')
208 self
.axis
.set_yscale('linear')
209 self
.axis
.set_xscale('linear')
212 d
.yline
= range(len(d
.y
))
213 for i
, yi
in enumerate(d
.y
):
214 d
.yline
[i
], = self
.axis
.plot(d
.x
, yi
, ls
=d
.linestyle
,
217 self
.axis
.set_title(self
.title
)
218 self
.axis
.set_xlabel(self
.xlabel
)
219 self
.axis
.set_ylabel(self
.ylabel
)
220 self
.axis
.grid(self
.grid
)
222 print 'x', self
.xscale
, 'y', self
.yscale
223 self
.axis
.set_yscale(self
.yscale
)
224 self
.axis
.set_xscale(self
.xscale
)
226 if len(self
.data
) > 0:
228 if len(self
.data
[0].y
) > 0:
234 if self
.axes_limits
.x
[self
.xscale
] == None:
235 if self
.debug
: print 'autoscaling...'
236 self
.axis
.autoscale_view(scalex
=True, scaley
=False)
237 self
.xmin
, self
.xmax
= self
.axis
.get_xlim()
239 if self
.debug
: print 'using old axis limit', self
.axes_limits
.x
240 self
.xmin
, self
.xmax
= self
.axes_limits
.x
[self
.xscale
]
241 if self
.xscale
== 'log':
242 if self
.xmin
<= 0: self
.xmin
= LOGMIN
243 if self
.xmax
<= self
.xmin
: self
.xmax
= self
.xmin
+1
244 self
.axis
.set_xlim(xmin
=self
.xmin
, xmax
=self
.xmax
)
246 if self
.debug
: print 'xmin:', self
.xmin
, 'xmax:', self
.xmax
247 self
.xminEntry
.set_text(str(self
.xmin
))
248 self
.xmaxEntry
.set_text(str(self
.xmax
))
251 if self
.axes_limits
.y
[self
.yscale
] == None:
252 if self
.debug
: print 'autoscaling...'
253 self
.axis
.autoscale_view(scaley
=True, scalex
=False)
254 self
.ymin
, self
.ymax
= self
.axis
.get_ylim()
256 if self
.debug
: print 'using old axis limit'
257 self
.ymin
, self
.ymax
= self
.axes_limits
.y
[self
.yscale
]
258 if self
.yscale
== 'log':
259 if self
.ymin
<= 0: self
.ymin
= LOGMIN
260 if self
.ymax
<= self
.ymin
: self
.ymax
= self
.ymin
+1
261 self
.axis
.set_ylim(ymin
=self
.ymin
, ymax
=self
.ymax
)
263 if self
.debug
: print 'ymin:', self
.ymin
, 'ymax:', self
.ymax
264 self
.yminEntry
.set_text(str(self
.ymin
))
265 self
.ymaxEntry
.set_text(str(self
.ymax
))
267 def load(self
, filename
):
269 x
, y
, xlab
, ylab
= xyLabelFromFile(filename
)
271 self
.writeStatusBar("Can't open file '%s'" % filename
)
274 self
.writeStatusBar("File '%s': file format unknown." % filename
)
277 filename
= os
.path
.basename(filename
)
278 self
.data
.append(DataSet(x
,y
, filename
))
279 self
.title
= filename
280 if xlab
is not None: self
.xlabel
= xlab
281 if ylab
is not None: self
.ylabel
= ylab
282 self
.axes_limits
.x
= dict(linear
=None, log
=None)
283 self
.axes_limits
.y
= dict(linear
=None, log
=None)
284 self
.writeStatusBar("File '%s' loaded." % filename
)
290 self
.writeStatusBar('Warning: '+msg
)
293 # The following are menu callback methods
295 def on_title_and_axes_labels_activate(self
, widget
, *args
):
296 TitleAndAxesWindow(self
)
299 # The following are GUI callback methods
301 def on_xscale_toggled(self
, widget
, *args
):
303 self
.axes_limits
.x
[self
.xscale
] = self
.axis
.get_xlim()
304 if self
.xscaleCheckB
.get_active(): self
.xscale
= 'log'
305 else: self
.xscale
= 'linear'
306 if self
.debug
: print "XScale:", self
.xscale
307 self
.axis
.set_xscale(self
.xscale
)
310 self
.writeStatusBar('X Axis Scale: %s.' % self
.xscale
)
313 def on_yscale_toggled(self
, widget
, *args
):
315 self
.axes_limits
.y
[self
.yscale
] = self
.axis
.get_ylim()
316 if self
.yscaleCheckB
.get_active(): self
.yscale
= 'log'
317 else: self
.yscale
= 'linear'
318 if self
.debug
: print "YScale:", self
.yscale
319 self
.axis
.set_yscale(self
.yscale
)
323 if self
.negative
and self
.yscale
== 'log':
324 self
.warn('Negative values not displayed in log-scale, lines '+\
325 'may appear discontinuos!')
327 self
.writeStatusBar('Y Axis Scale: %s.' % self
.yscale
)
330 def on_points_toggled(self
, widget
, *args
):
331 self
.showPoints
= not self
.showPoints
332 if self
.debug
: print "Show Points:", self
.showPoints
335 # Restore the previous marker style
336 d
.yline
[0].set_marker(d
.marker
)
338 # Turn off the marker visualization
339 d
.yline
[0].set_marker('')
342 def on_lines_toggled(self
, widget
, *args
):
343 self
.showLines
= not self
.showLines
344 if self
.debug
: print "Show Lines:", self
.showLines
347 # Restore the previous linestyle
348 d
.yline
[0].set_linestyle(d
.linestyle
)
350 # Turn off the line visualization
351 d
.yline
[0].set_linestyle('')
354 def on_grid_toggled(self
, widget
, *args
):
355 self
.grid
= not self
.grid
356 if self
.debug
: print "Show Grid:", self
.grid
357 self
.axis
.grid(self
.grid
)
360 def on_xmin_entry_activate(self
, widget
, *args
):
361 if self
.debug
: print 'X Min Entry Activated'
362 s
= self
.xminEntry
.get_text()
366 self
.statusBar
.push(self
.context
, 'Wrong X axis min limit.')
367 self
.xminEntry
.set_text('')
369 if self
.xscale
== 'log' and val
<= 0:
371 self
.warn('Only values > 0 are allowed in log scale.')
372 self
.xminEntry
.set_text(str(val
))
374 self
.axis
.set_xlim(xmin
=val
)
377 def on_xmax_entry_activate(self
, widget
, *args
):
378 if self
.debug
: print 'X Max Entry Activated'
379 s
= self
.xmaxEntry
.get_text()
383 self
.statusBar
.push(self
.context
, 'Wrong X axis max limit.')
384 self
.xmaxEntry
.set_text('')
386 if self
.xscale
== 'log' and val
<= 0:
388 self
.warn('Only values > 0 are allowed in log scale.')
389 self
.xmaxEntry
.set_text(str(val
))
391 self
.axis
.set_xlim(xmax
=val
)
394 def on_ymin_entry_activate(self
, widget
, *args
):
395 if self
.debug
: print 'Y Min Entry Activated'
396 s
= self
.yminEntry
.get_text()
400 self
.statusBar
.push(self
.context
, 'Wrong Y axis min limit.')
401 self
.yminEntry
.set_text('')
403 if self
.yscale
== 'log' and val
<= 0:
405 self
.warn('Only values > 0 are allowed in log scale.')
406 self
.yminEntry
.set_text(str(val
))
408 self
.axis
.set_ylim(ymin
=val
)
411 def on_ymax_entry_activate(self
, widget
, *args
):
412 if self
.debug
: print 'Y Max Entry Activated'
413 s
= self
.ymaxEntry
.get_text()
417 self
.statusBar
.push(self
.context
, 'Wrong Y axis max limit.')
418 self
.ymaxEntry
.set_text('')
420 if self
.yscale
== 'log' and val
<= 0:
422 self
.warn('Only values > 0 are allowed in log scale.')
423 self
.ymaxEntry
.set_text(str(val
))
425 self
.axis
.set_ylim(ymax
=val
)
428 def on_apply_button_clicked(self
, widget
, *args
):
429 sxmin
= self
.xminEntry
.get_text()
430 sxmax
= self
.xmaxEntry
.get_text()
431 symin
= self
.yminEntry
.get_text()
432 symax
= self
.ymaxEntry
.get_text()
434 xmin_val
= float(sxmin
)
435 xmax_val
= float(sxmax
)
436 ymin_val
= float(symin
)
437 ymax_val
= float(symax
)
439 self
.statusBar
.push(self
.context
, 'Wrong axis limit')
441 if self
.xscale
== 'log':
442 if xmin_val
<= 0: xmin_val
= LOGMIN
443 if xmax_val
<= 0: xmax_val
= xmin_val
*10
444 if ymin_val
<= 0: ymin_val
= LOGMIN
445 if ymax_val
<= 0: ymax_val
= ymin_val
*10
446 self
.xmin
, self
.xmax
, self
.ymin
, self
.ymax
= \
447 xmin_val
, xmax_val
, ymin_val
, ymax_val
448 self
.axis
.set_xlim(xmin
=xmin_val
)
449 self
.axis
.set_xlim(xmax
=xmax_val
)
450 self
.axis
.set_ylim(ymin
=ymin_val
)
451 self
.axis
.set_ylim(ymax
=ymax_val
)
455 # The following are MENU callback methods
457 def on_addDataSet_activate(self
, widget
, *args
):
459 def on_new_activate(self
, widget
, *args
):
461 self
.reset_scale_buttons()
463 def on_dimensionsResolution_activate(self
, widget
, *args
):
464 DimentionAndResolution(self
)
465 def on_autoscale_activate(self
, widget
, *args
):
466 self
.axis
.autoscale_view()
468 def on_coordinatePicker_activate(self
, widget
, *args
):
469 if self
.cooPickerCheckB
.get_active():
470 self
.cid
= self
.canvas
.mpl_connect('button_press_event',
472 self
.writeStatusBar('Click on the plot to log coordinates on '+\
474 elif self
.cid
!= None:
475 self
.canvas
.mpl_disconnect(self
.cid
)
476 self
.writeStatusBar('Coordinate picker disabled.')
478 print "Error: tried to disconnect an unexistent event."
479 def on_moreGrid_activate(self
, widget
, *args
):
480 if self
.moreGridCheckB
.get_active():
481 self
.axis
.xaxis
.grid(True, which
='minor', ls
='--', alpha
=0.5)
482 self
.axis
.yaxis
.grid(True, which
='minor', ls
='--', alpha
=0.5)
483 self
.axis
.xaxis
.grid(True, which
='major', ls
='-', alpha
=0.5)
484 self
.axis
.yaxis
.grid(True, which
='major', ls
='-', alpha
=0.5)
486 self
.axis
.xaxis
.grid(False, which
='minor')
487 self
.axis
.yaxis
.grid(False, which
='minor')
488 self
.axis
.xaxis
.grid(True, which
='major', ls
=':')
489 self
.axis
.yaxis
.grid(True, which
='major', ls
=':')
491 def on_info_activate(self
, widget
, *args
):
493 def on_plot_properties_activate(self
, widget
, *args
):
494 PlotPropertiesDialog(self
)
495 def on_makeInterpolation_activate(self
, widget
, *args
):
496 InterpolationDialog(self
)
499 def get_coordinates_cb(event
):
501 Callback for the coordinate picker tool. This function is not a class
502 method because I don't know how to connect an MPL event to a method instead
505 if not event
.inaxes
: return
506 print "%3.4f\t%3.4f" % (event
.xdata
, event
.ydata
)
509 # Abstract dialogs classes
511 class DialogWithPlotList(DialogWindowWithCancel
):
512 """Abstract class for dialogs with a list of current plots in them."""
514 def __init__(self
, dialogName
, gladefile
, callerApp
):
515 DialogWindowWithCancel
.__init
__(self
, dialogName
, gladefile
, callerApp
)
517 self
.treeView
= self
.widgetTree
.get_widget('treeview')
518 self
.create_plotlist()
520 def create_plotlist(self
):
521 # Add a column to the treeView
522 self
.addListColumn('Plot List', 0)
524 # Create the listStore Model to use with the treeView
525 self
.plotList
= gtk
.ListStore(str)
528 self
.insertPlotList()
530 # Attatch the model to the treeView
531 self
.treeView
.set_model(self
.plotList
)
533 def addListColumn(self
, title
, columnId
):
534 """This function adds a column to the list view.
535 First it create the gtk.TreeViewColumn and then set
536 some needed properties"""
538 column
= gtk
.TreeViewColumn(title
, gtk
.CellRendererText(), text
=0)
539 column
.set_resizable(True)
540 column
.set_sort_column_id(columnId
)
541 self
.treeView
.append_column(column
)
543 def insertPlotList(self
):
544 for data
in self
.callerApp
.data
:
545 self
.plotList
.append([data
.name
])
547 def on_applyButton_clicked(self
, widget
, *args
):
548 self
.window
.destroy()
550 def on_cancelButton_clicked(self
, widget
, *args
):
551 self
.window
.destroy()
553 def on_okButton_clicked(self
, widget
, *args
):
554 self
.window
.destroy()
559 class InterpolationDialog(DialogWithPlotList
):
560 def __init__(self
, callerApp
):
561 DialogWithPlotList
.__init
__(self
, 'InterpolationDialog',
562 gladefile
, callerApp
)
563 # To be implemented ...
566 class PlotPropertiesDialog(DialogWithPlotList
):
567 def __init__(self
, callerApp
):
568 DialogWithPlotList
.__init
__(self
, 'PlotPropertiesDialog',
569 gladefile
, callerApp
)
573 # Retrive the data list
574 self
.data
= self
.callerApp
.data
576 # Save all the lines properties in case of Cancell-button
577 self
.orig_lines
= range(len(self
.data
))
578 for i
,d
in enumerate(self
.data
):
579 self
.orig_lines
[i
] = [Line2D([0], [0]), d
.linestyle
, d
.marker
]
580 self
.orig_lines
[i
][0].update_from(d
.yline
[0])
582 self
.selected_data
= None
583 self
.selected_line
= None
584 self
.allowReplot
= False
586 def load_widgets(self
):
587 self
.lineWidthSpinButt
= \
588 self
.widgetTree
.get_widget('lineWidthSpinButton')
589 self
.lineStyleComboB
= \
590 self
.widgetTree
.get_widget('lineStyleComboBox')
591 self
.lineColorComboB
= \
592 self
.widgetTree
.get_widget('lineColorComboBox')
593 self
.markerTypeComboB
= \
594 self
.widgetTree
.get_widget('markerTypeComboBox')
595 self
.markerSizeSpinButt
= \
596 self
.widgetTree
.get_widget('markerSizeSpinButton')
597 self
.markerEdgeColorComboB
= \
598 self
.widgetTree
.get_widget('markerEdgeColComboBox')
599 self
.markerFaceColorComboB
= \
600 self
.widgetTree
.get_widget('markerFaceColComboBox')
601 self
.markerEdgeWidthSpinButt
= \
602 self
.widgetTree
.get_widget('markerEdgeWidthSpinButton')
604 def update_properties(self
):
605 # Set the Spin Buttons with the values for the selected data
606 self
.lineWidthSpinButt
.set_value(self
.selected_line
.get_linewidth())
607 self
.markerSizeSpinButt
.set_value(self
.selected_line
.get_markersize())
608 mew
= self
.selected_line
.get_markeredgewidth()
609 self
.markerEdgeWidthSpinButt
.set_value(mew
)
611 # Set the Combo Boxes with the values for the selected data
612 self
.set_current_linestyle(self
.selected_data
)
613 self
.set_current_marker(self
.selected_data
)
614 self
.set_current_lineColor(self
.selected_line
)
615 self
.set_current_markerEdgeColor(self
.selected_line
)
616 self
.set_current_markerFaceColor(self
.selected_line
)
618 def set_current_lineColor(self
, data_line
):
619 lc
= data_line
.get_color()
622 ic
= colors
.index(lc
)
623 self
.lineColorComboB
.set_active(ic
)
625 print "WARNING: Unsupported color '%s'." % lc
627 def set_current_markerEdgeColor(self
, data_line
):
628 mec
= data_line
.get_mec()
631 ic
= colors
.index(mec
)
632 self
.markerEdgeColorComboB
.set_active(ic
)
634 print "WARNING: Unsupported color '%s'." % mec
636 def set_current_markerFaceColor(self
, data_line
):
637 mfc
= data_line
.get_mfc()
640 ic
= colors
.index(mfc
)
641 self
.markerFaceColorComboB
.set_active(ic
)
643 print "WARNING: Unsupported color '%s'." % mfc
645 def set_current_linestyle(self
, data
):
647 line_styles
= ['-', '--', ':', '-.']
649 if ls
in line_styles
:
650 il
= line_styles
.index(ls
)
651 self
.lineStyleComboB
.set_active(il
)
653 print "WARNING: Linestyle '%s' not supported." % ls
655 def set_current_marker(self
, data
):
657 markers
= 'os^v<>.+xdDhHp'
659 im
= markers
.index(m
)
660 self
.markerTypeComboB
.set_active(im
)
662 print "WARNING: Marker type '%s' not supported." % m
668 def on_treeview_cursor_changed(self
, *args
):
669 # Retrive the current plot/line reference
670 plot_index
= self
.treeView
.get_cursor()[0][0]
671 self
.selected_line
= self
.data
[plot_index
].yline
[0]
672 self
.selected_data
= self
.callerApp
.data
[plot_index
]
674 # Update the plot with the current line/plot properties
675 self
.allowReplot
= False
676 self
.update_properties()
677 self
.allowReplot
= True
679 # Why self.allowReplot ?
680 # Changing combo box or spin button state will emit the corresponding
681 # signal caught by their GUI callback. The callback updates the plot.
682 # We use the self.allowReplot flag to inibit the CB from updating the
683 # plot while we are setting up the properties for the current line.
685 def on_lineWidth_value_changed(self
, *args
):
686 if self
.allowReplot
and self
.selected_line
is not None:
688 lw
= self
.lineWidthSpinButt
.get_value()
690 self
.selected_line
.set_linewidth(lw
)
691 self
.callerApp
.canvas
.draw()
693 def on_lineStyle_changed(self
, *args
):
694 if self
.allowReplot
and self
.selected_line
is not None:
695 # Switch ON the "show lines" check button on the main windows if
696 # we change line style to prevent inconsistent state.
697 if not self
.callerApp
.linesCheckB
.get_active():
698 self
.callerApp
.linesCheckB
.set_active(True)
701 ls
= self
.lineStyleComboB
.get_active_text()
703 line_styles
= {'Continuous': '-', 'Dashed': '--',
704 'Dotted': ':', 'Dot-Line': '-.'}
706 if ls
in line_styles
.keys():
707 self
.selected_line
.set_linestyle(line_styles
[ls
])
708 self
.callerApp
.canvas
.draw()
709 self
.selected_data
.linestyle
= line_styles
[ls
]
711 print "WARNING: Unsupported line style '%s'." % ls
713 def on_lineColor_changed(self
, *args
):
714 if self
.allowReplot
and self
.selected_line
is not None:
716 text
= self
.lineColorComboB
.get_active_text()
717 color
= text
[text
.find('(') + 1]
719 self
.selected_line
.set_color(color
)
720 self
.callerApp
.canvas
.draw()
722 def on_markerType_changed(self
, *args
):
723 if self
.allowReplot
and self
.selected_line
is not None:
724 # Switch ON the "show points" check button on the main windows if
725 # we change marker properties to prevent inconsistent state.
726 if not self
.callerApp
.pointsCheckB
.get_active():
727 self
.callerApp
.pointsCheckB
.set_active(True)
730 text
= self
.markerTypeComboB
.get_active_text()
731 m
= text
[text
.find('(') + 1]
733 self
.selected_line
.set_marker(m
)
734 self
.callerApp
.canvas
.draw()
735 self
.selected_data
.marker
= m
737 def on_markerSize_value_changed(self
, *args
):
738 if self
.allowReplot
and self
.selected_line
is not None:
740 ms
= self
.markerSizeSpinButt
.get_value()
742 self
.selected_line
.set_markersize(ms
)
743 self
.callerApp
.canvas
.draw()
745 def on_markerEdgeWidth_value_changed(self
, *args
):
746 if self
.allowReplot
and self
.selected_line
is not None:
747 prop
= 'markeredgewidth'
748 mew
= self
.markerEdgeWidthSpinButt
.get_value()
750 self
.selected_line
.set_markeredgewidth(mew
)
751 self
.callerApp
.canvas
.draw()
753 def on_markerEdgeColor_changed(self
, *args
):
754 if self
.allowReplot
and self
.selected_data
is not None:
755 prop
= 'markeredgecolor'
756 text
= self
.markerEdgeColorComboB
.get_active_text()
757 color
= text
[text
.find('(') + 1]
759 self
.selected_line
.set_markeredgecolor(color
)
760 self
.callerApp
.canvas
.draw()
762 def on_markerFaceColor_changed(self
, *args
):
763 if self
.allowReplot
and self
.selected_data
is not None:
764 prop
= 'markerfacecolor'
765 text
= self
.markerFaceColorComboB
.get_active_text()
766 color
= text
[text
.find('(') + 1]
768 self
.selected_line
.set_markerfacecolor(color
)
769 self
.callerApp
.canvas
.draw()
771 def on_cancelButton_clicked(self
, widget
, *args
):
772 # Restore the old style
773 for orig_line
, d
in zip(self
.orig_lines
, self
.data
):
774 d
.yline
[0].update_from(orig_line
[0])
775 d
.linestyle
= orig_line
[1]
776 d
.marker
= orig_line
[2]
778 self
.callerApp
.canvas
.draw()
779 self
.window
.destroy()
781 def on_okButton_clicked(self
, widget
, *args
):
782 self
.window
.destroy()
785 class TitleAndAxesWindow(DialogWindowWithCancel
):
787 Dialog for setting Title and axes labels.
789 def __init__(self
, callerApp
):
790 DialogWindowWithCancel
.__init
__(self
, 'TitleAndAxesWindow', gladefile
,
793 self
.titleEntry
= self
.widgetTree
.get_widget('titleEntry')
794 self
.xlabelEntry
= self
.widgetTree
.get_widget('xlabelEntry')
795 self
.ylabelEntry
= self
.widgetTree
.get_widget('ylabelEntry')
797 # Update the entries with the current values
798 def sync(field
, entry
):
799 if field
!= None: entry
.set_text(field
)
801 sync(self
.callerApp
.title
, self
.titleEntry
)
802 sync(self
.callerApp
.xlabel
, self
.xlabelEntry
)
803 sync(self
.callerApp
.ylabel
, self
.ylabelEntry
)
805 def on_okButton_clicked(self
, widget
, *args
):
807 self
.callerApp
.axis
.set_title(self
.titleEntry
.get_text())
808 self
.callerApp
.axis
.set_xlabel(self
.xlabelEntry
.get_text())
809 self
.callerApp
.axis
.set_ylabel(self
.ylabelEntry
.get_text())
810 self
.callerApp
.canvas
.draw()
812 self
.callerApp
.title
= self
.titleEntry
.get_text()
813 self
.callerApp
.xlabel
= self
.ylabelEntry
.get_text()
814 self
.callerApp
.ylabel
= self
.xlabelEntry
.get_text()
816 self
.window
.destroy()
818 class DimentionAndResolution(DialogWindowWithCancel
):
820 Dialog for setting Figure dimentions and resolution.
822 def __init__(self
, callerApp
):
823 DialogWindowWithCancel
.__init
__(self
, 'DimentionsDialog', gladefile
,
825 self
.xdimSpinButton
= self
.widgetTree
.get_widget('xdimSpinButton')
826 self
.ydimSpinButton
= self
.widgetTree
.get_widget('ydimSpinButton')
827 self
.resolutionSpinButton
= self
.widgetTree
.get_widget(
828 'resolutionSpinButton')
829 self
.inchesRadioB
= self
.widgetTree
.get_widget('inRadioButton')
831 self
.xdim_o
, self
.ydim_o
= self
.callerApp
.figure
.get_size_inches()
832 self
.dpi_o
= self
.callerApp
.figure
.get_dpi()
834 self
.set_initial_values()
836 def set_values(self
, xdim
, ydim
, dpi
):
837 if not self
.inchesRadioB
.get_active():
838 xdim
*= CM_PER_INCHES
839 ydim
*= CM_PER_INCHES
840 self
.xdimSpinButton
.set_value(xdim
)
841 self
.ydimSpinButton
.set_value(ydim
)
842 self
.resolutionSpinButton
.set_value(dpi
)
844 def set_initial_values(self
):
845 self
.set_values(self
.xdim_o
, self
.ydim_o
, self
.dpi_o
)
847 def set_default_values(self
):
848 xdim
, ydim
= rcParams
['figure.figsize']
849 dpi
= rcParams
['figure.dpi']
850 self
.set_values(xdim
, ydim
, dpi
)
852 def get_values(self
):
853 self
.xdim
= self
.xdimSpinButton
.get_value()
854 self
.ydim
= self
.ydimSpinButton
.get_value()
855 self
.resolution
= self
.resolutionSpinButton
.get_value()
856 if not self
.inchesRadioB
.get_active():
857 self
.xdim
/= CM_PER_INCHES
858 self
.ydim
/= CM_PER_INCHES
860 def set_figsize(self
):
861 self
.callerApp
.figure
.set_size_inches(self
.xdim
, self
.ydim
)
862 self
.callerApp
.figure
.set_dpi(self
.resolution
)
864 def on_unity_toggled(self
, widget
, *args
):
865 xdim
= self
.xdimSpinButton
.get_value()
866 ydim
= self
.ydimSpinButton
.get_value()
867 if self
.inchesRadioB
.get_active():
868 xdim
/= CM_PER_INCHES
869 ydim
/= CM_PER_INCHES
871 xdim
*= CM_PER_INCHES
872 ydim
*= CM_PER_INCHES
873 self
.xdimSpinButton
.set_value(xdim
)
874 self
.ydimSpinButton
.set_value(ydim
)
876 def on_okButton_clicked(self
, widget
, *args
):
880 self
.callerApp
.canvas
.draw()
881 self
.window
.destroy()
883 def on_restoreButton_clicked(self
, widget
, *args
):
884 self
.set_default_values()
887 class OpenFileDialog(GenericOpenFileDialog
):
889 This class implements the "Open File" dialog.
891 def __init__(self
, callerApp
):
892 GenericOpenFileDialog
.__init
__(self
, 'OpenFileDialog', gladefile
,
895 def openSelectedFile(self
):
896 loaded
= self
.callerApp
.load( self
.filename
)
898 self
.callerApp
.plot_data(-1)
899 self
.window
.destroy()
902 class AboutDialog(GenericSecondaryWindow
):
904 Object for the "About Dialog".
907 GenericSecondaryWindow
.__init
__(self
, 'AboutDialog', gladefile
)
908 self
.window
.set_version(read_version())
914 """ Extract version from README file. """
915 file = open(rootdir
+'README')
918 if line
.strip().startswith('*Latest Version*'):
928 p
= PlotFileApp(x
, y
, title
='Random Sequence', debug
=True)
932 p
= PlotFileApp(debug
=True)
933 # Try to open a sample file
934 if len(sys
.argv
) < 2 and os
.path
.isfile(sourcedir
+'samples/data.txt'):
935 p
.load(sourcedir
+'samples/data.txt')
939 if __name__
== '__main__': main()