IA: Added save file support (only in pickle format for now)
[pyplotsuite.git] / pyplotsuite / imageanalyzer2.py
blobb4b9689caa8d409cd41e8130b3bd1ea82a26cf4f
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 # Image Analizer -- A tool to visualize and analyze images
5 # Version: 0.1-alpha (see README)
7 # Copyright (C) 2006-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.
14 import sys
15 import os.path
16 import imp
17 rootdir = os.path.abspath(imp.find_module('pyplotsuite')[1]) + '/'
18 sourcedir = rootdir + 'imageanalyzer/'
19 gladefile = sourcedir + 'image_analyzer.glade'
21 dark_suffix = '-buio'
22 debug = True
24 from PIL import Image
26 # Generic window description classes
27 from pyplotsuite.common.gtk_generic_windows import \
28 GenericMainPlotWindow, MyNavigationToolbar, \
29 GenericSecondaryWindow, DialogWindowWithCancel
31 # Dialogs classes
32 from imageanalyzer.histogramdialog import HistogramApp
33 from imageanalyzer.sectionsdialog import SectionApp
35 # Mathematical functions
36 from numpy import arange, array, sqrt, log10
38 try:
39 import scipy.ndimage.filters as filters
40 except:
41 print 'WARNING: No scipy.ndimage.filters, filters disabled.'
43 # Import matplotlib widgets and functions
44 from matplotlib.cm import get_cmap
45 from matplotlib.collections import LineCollection
46 from matplotlib.colors import colorConverter
48 # Some custom utility modules
49 from pyplotsuite.common.arrayfromfile import arrayfromfile, arrayFromZemax
50 from pyplotsuite.common.img import image2array
51 from pyplotsuite.common.version import read_version
52 from imageanalyzer.circularlist import CircularList
53 from imageanalyzer.section import ArraySection
54 from imageanalyzer.loadfile import *
56 # Import the profiler
57 #import profile
58 #profile.Profile.bias = 5e-6
60 def dprint(*args):
61 global debug
62 if debug: print args
65 # Implementation Classes
67 class ImageApp(GenericMainPlotWindow):
68 """
69 Main application window object.
70 """
71 def __init__(self, filename=None):
72 GenericMainPlotWindow.__init__(self, 'ImageWindow', gladefile,
73 autoconnect=True, makeAxis=False, makeToolbar=False)
75 self.image_loaded = False # Must stay before gui_setup()
76 self.gui_setup()
77 self.filename = filename
79 # Create additional attributes with default values
80 self.gridon = True
81 self.origin = 'upper' # Image origin ('upper' or 'lower')
82 self.gridcolor = 'white'
83 self.xPixelDim = 1
84 self.yPixelDim = 1
85 self.min = 0
86 self.max = 2**14+1
87 self.interp = 'Nearest' # corresponds to .set_active(12)
88 self.colormap = get_cmap('jet') # corresponds to .set_active(8)
89 #self.position = None
90 self.histogramApp = None
91 self.sectionApp = None
92 self.colors = ('green', 'red', 'blue', 'magenta', 'cyan', 'yellow',
93 'orange', 'gray')
95 self.segments = []
96 self.linescolors = []
97 self.measuring = False # - indicate if the measure button is pushed
98 self.secondPoint = False # - indicate if we're waiting for the second
99 # point of a distance measure
100 self.title = None
101 self.xlabel = None
102 self.ylabel = None
103 self.colorlist = None
104 self.sectionsLC = None # LinesCollection representing the sections
105 self.aspect = 1
107 # Show the window and each child
108 self.window.show_all()
110 # Load the eventual image
111 if not filename == None:
112 self.load_image(filename)
113 else:
114 self.statusBar.push(self.context,
115 'Use menu File -> Open to load an image.')
117 def gui_setup(self):
118 # Create the matplotlib toolbar
119 self.mpl_toolbar = MyNavigationToolbar(self.canvas, self.window,
120 ButtonCallBack = self.on_measureButton_toggled,
121 icon_fname = sourcedir+'icons/measure-icon-20.png',
122 label = 'Measure Tool',
123 tooltip = 'Enable/Disable the "Measure Tool".'
125 toolbar_container = self.widgetTree.get_widget('MyToolbarAlignment')
126 toolbar_container.add(self.mpl_toolbar)
128 # Create an handle for the grid check box
129 self.gridCheckBox = self.widgetTree.get_widget('grid')
131 # Create handles for the SpinBoxes
132 self.minSpinButton = self.widgetTree.get_widget('minSpinButton')
133 self.maxSpinButton = self.widgetTree.get_widget('maxSpinButton')
135 # Set the handler for the colormap ComboBox
136 self.cmapComboBox = self.widgetTree.get_widget('cmapComboBox')
137 self.cmapComboBox.set_active(8) # 'jet'
139 # Set the handler for the interpolation ComboBox
140 self.interpComboBox = self.widgetTree.get_widget('interpComboBox')
141 self.interpComboBox.set_active(12) # 'Nearest'
143 self.openfileDialog = None
144 self.savefileDialog = None
147 # The following are plotting methods
149 def _load_file(self, fname):
151 Low-level function used by load_image to handle file open, format
152 autodetection and loading of basic data.
154 load_image requires that _load_file set the following:
155 - self.imagemode
156 - self.image_array
157 and optionally also self.image (the PIL image object).
159 try:
160 self.image_array, self.imagemode, self.image = load_file(fname)
161 except IOError, error_string:
162 self.statusBar.push(self.context, error_string)
163 return False
164 except UnknownFileType:
165 self.statusBar.push(self.context,
166 'File "%s" is of unkown type.' % fname)
167 return False
168 else:
169 return True
171 def load_image(self, filename):
173 Load the given image filename, and plot it. It set also the statical
174 attributes for a given image (.min, .max, .bits, .isColorImage)
177 # Try to open the file and load the image ...
178 if not self._load_file(filename): return
180 # ... and if the image il loaded ...
181 self.window.set_title(filename)
182 self.figure.clf()
183 self.axim = self.figure.add_subplot(111)
185 if self.imagemode == 'L':
186 self.nbit = 8
187 self.isColorImage = False
188 elif self.imagemode == 'I;16':
189 self.nbit = 14
190 self.isColorImage = False
191 elif self.imagemode == 'floatarray':
192 self.nbit = 8
193 self.isColorImage = False
194 else:
195 self.nbit = 8
196 self.isColorImage = True
198 if self.isColorImage:
199 self.statusBar.push(self.context,
200 'Image RGB(A): ignoring the range (min and max).')
201 else:
202 self.min = self.image_array.min()
203 self.max = self.image_array.max()
204 if self.imagemode == 'floatarray':
205 s = 'float values.'
206 else:
207 s = str(self.nbit)+' bit.'
208 self.statusBar.push(self.context,
209 'Image L (Luminance) with '+s)
211 self.autoset_extent()
212 self.plot_image(self.min, self.max)
213 self.image_loaded = True
215 def plot_image(self, min=None, max=None, interp=None, cmap_name=None,
216 origin=None, extent=None):
218 Plot the image self.image_array. The optional parameters allow to
219 choose min and max range value, interpolation method and colormap.
220 If not specified the default values are took from the object
221 attributes (self.min, self.max, self.cmap, self.interp).
223 Assumes the statical attributes for the given image to be set (for
224 example .isColorImage).
226 if not min == None: self.set_min(min)
227 if not max == None: self.set_max(max)
228 if not interp == None: self.interp = interp
229 if not origin == None: self.origin = origin
230 if not cmap_name == None: self.colormap = get_cmap(cmap_name)
231 if not extent == None: self.extent = extend
233 self.axim.clear()
235 self.i = self.axim.imshow(self.image_array,
236 interpolation=self.interp,
237 vmin=self.min, vmax=self.max,
238 cmap=self.colormap,
239 origin=self.origin,
240 extent=self.extent,
241 aspect=self.aspect)
243 if not self.isColorImage:
244 # Plot the colorbar
245 try:
246 # Try to plot the colorbar on an existing second axis
247 self.figure.colorbar(self.i, self.figure.axes[1])
248 except:
249 # Otherwise let colorbar() to create its default axis
250 self.figure.colorbar(self.i)
252 if self.gridon:
253 self.axim.grid(color=self.gridcolor)
254 # self.axim.axis('image')
255 self.set_title_and_labels()
256 if self.sectionsLC != None:
257 self.axim.add_collection(self.sectionsLC)
258 self.canvas.draw()
260 def autoset_extent(self):
262 Auto-set the image extent using image dimension and pixel size.
264 if self.isColorImage:
265 ly, lx, ch = self.image_array.shape
266 else:
267 ly, lx = self.image_array.shape
268 self.extent = (0, lx*self.xPixelDim, 0, ly*self.yPixelDim)
270 def set_min(self, min):
271 self.min = min
272 self.minSpinButton.set_value(min)
274 def set_max(self, max):
275 self.max = max
276 self.maxSpinButton.set_value(max)
278 def set_title_and_labels(self):
279 if self.title != None:
280 self.axim.set_title(self.title)
281 if self.xlabel != None:
282 self.axim.set_xlabel(self.xlabel)
283 if self.ylabel != None:
284 self.axim.set_ylabel(self.ylabel)
287 # Methods for the "Measure" tool and to manage sections
289 def on_canvas_clicked(self, event):
290 if not event.inaxes == self.axim: return
291 if not self.secondPoint:
292 self.secondPoint = True
293 print "x:", event.xdata, ", y:", event.ydata
294 self.x1 = int(event.xdata) + 0.5
295 self.y1 = int(event.ydata) + 0.5
296 m = 'First point: X1 = '+str(self.x1)+', Y1 = '+str(self.y1)+'.'
297 m += ' Select the second point.'
298 self.statusBar.push(self.context, m)
299 else:
300 print "x:", event.xdata, ", y:", event.ydata
301 x2 = int(event.xdata) + 0.5
302 y2 = int(event.ydata) + 0.5
304 self.segments.append(((self.x1, self.y1), (x2, y2)))
305 self.linescolors.append(self.colorlist.next())
306 self.sectionsLC = LineCollection(self.segments,
307 colors=self.linescolors)
308 self.sectionsLC.set_linewidth(3)
309 self.axim.add_collection(self.sectionsLC)
310 self.canvas.draw()
312 # Print distance to status bar
313 self.calculateDistance(self.segments[-1])
315 if self.sectionApp != None:
316 self.sectionApp.addsection(self.calculateSection(-1))
318 self.secondPoint = False
319 self.x1, self.y1 = None, None
321 def calculateDistance(self, section, useStatusBar=True):
322 (x1, y1), (x2, y2) = section
323 dx = (x2 - x1)
324 dy = (y2 - y1)
325 dist = sqrt(dx**2 + dy**2)
326 if useStatusBar:
327 m = 'X1 = %d, Y1 = %d; ' % (self.x1, self.y1)
328 m += 'X2 = %d, Y2 = %d; ' % (x2, y2)
329 m += 'Distance: %5.1f μm.' % (dist)
330 self.statusBar.push(self.context, m)
331 return dist
333 def calculateSection(self, segmentindex):
334 (x1, y1), (x2, y2) = self.segments[segmentindex]
336 print 'xpdim:', self.xPixelDim, 'ypdim:', self.yPixelDim
337 px = lambda xi: int(round(xi/float(self.xPixelDim)))
338 py = lambda yi: int(round(yi/float(self.yPixelDim)))
340 px1, px2, py1, py2 = px(x1), px(x2), py(y1), py(y2)
341 print 'Segment:', px1, px2, py1, py2
343 if self.origin == 'upper':
344 py1, py2 = self.image_array.shape[0] - array((py1, py2)) - 1
345 print 'Segment UpDown:', px1, px2, py1, py2
347 section = ArraySection(self.image_array, px1, px2, py1, py2,
348 self.xPixelDim, self.yPixelDim, debug=True)
350 # Recall the corresponding section color
351 section.color = self.linescolors[segmentindex]
353 return section
356 # The following are Toolbar callbacks
358 def on_cmapComboBox_changed(self, widget, *args):
359 colormap_name = self.cmapComboBox.get_active_text().lower()
360 self.colormap = get_cmap(colormap_name)
361 if not self.image_loaded: return
362 elif self.isColorImage:
363 self.colormap = get_cmap(colormap_name)
364 self.statusBar.push(self.context,
365 'Colormaps are ignored for color images.')
366 else:
367 self.i.set_cmap(self.colormap)
368 self.canvas.draw()
370 def on_interpComboBox_changed(self, widget, *args):
371 if not self.image_loaded: return None
372 interpolation_mode = self.interpComboBox.get_active_text()
373 dprint ("Interpolation:", interpolation_mode)
374 self.interp = interpolation_mode
375 self.i.set_interpolation(interpolation_mode)
376 self.canvas.draw()
378 def on_histogramButton_clicked(self, widget, *args):
379 if not self.image_loaded:
380 self.statusBar.push(self.context,
381 'You should open an image to visualyze the histogram.')
382 return None
383 elif self.isColorImage:
384 self.statusBar.push(self.context,
385 'Histogram view is not available for color images.')
386 return None
388 if self.histogramApp == None:
389 dprint ("new histogram")
390 self.histogramApp = HistogramApp(gladefile, self, self.nbit,
391 debug=debug)
392 else:
393 dprint ("old histogram")
394 self.histogramApp.window.show()
396 def on_applyButton_clicked(self, widget, *args):
397 min = self.minSpinButton.get_value()
398 max = self.maxSpinButton.get_value()
399 if min >= max:
400 self.minSpinButton.set_value(self.min)
401 self.maxSpinButton.set_value(self.max)
402 self.writeStatusBar("Invalid range value: must be Min < Max.")
403 elif self.min != min or self.max != max:
404 self.min, self.max = min, max
405 self.plot_image(self.min, self.max)
406 self.writeStatusBar("New range applied.")
408 def on_measureButton_toggled(self, widget, data=None):
409 self.measuring = not self.measuring
410 if self.measuring:
411 # Create the list of colors the first time
412 if self.colorlist == None:
413 self.colorlist = CircularList(len(self.colors))
414 for c in self.colors:
415 self.colorlist.append(colorConverter.to_rgba(c))
416 # Connect the cb to handle the measure
417 self.cid = self.canvas.mpl_connect('button_release_event',
418 self.on_canvas_clicked)
419 self.statusBar.push(self.context,
420 '"Measure Length" tool enabled: select the first point.')
421 else:
422 self.statusBar.push(self.context,
423 '"Measure Length" tool disabled.')
424 self.canvas.mpl_disconnect(self.cid)
427 # The following are menu callbacks
429 def on_openfile_activate(self, widget, *args):
430 if self.openfileDialog is None:
431 self.openfileDialog = OpenFileDialog(self)
432 else:
433 self.openfileDialog.window.show()
435 def on_savefile_activate(self, widget, *args):
436 if self.savefileDialog is None:
437 self.savefileDialog = SaveFileDialog(self)
438 else:
439 self.savefileDialog.window.show()
441 def on_close_activate(self, widget, *args):
442 self.figure.clf()
443 self.canvas.draw()
444 self.image_loaded = False
446 def on_quit_activate(self, widget, *args):
447 self.on_imagewindow_destroy(widget, *args)
449 def on_downup_activate(self, widget, *args):
450 if self.origin == 'upper': self.origin = 'lower'
451 else: self.origin = 'upper'
452 if self.image_loaded: self.plot_image()
453 if self.sectionApp != None:
454 print " *** WARNING: swap of segments not implemented."
456 def on_grid_activate(self, widget, data=None):
457 if self.gridon: self.axim.grid(False) # Toggle ON -> OFF
458 else: self.axim.grid(color=self.gridcolor) # Toggle OFF -> ON
459 self.canvas.draw()
460 self.gridon = not self.gridon
462 def on_whitegrid_activate(self, widget, *args):
463 if self.gridcolor == 'white': self.gridcolor = 'black'
464 else: self.gridcolor = 'white'
465 dprint ("grid color:", self.gridcolor)
467 if self.gridon:
468 self.axim.grid(self.gridon, color=self.gridcolor)
469 self.canvas.draw()
471 def on_information_activate(self, widget, *args):
472 about = AboutDialog()
474 def on_pixelDimension_activate(self, widget, *args):
475 PixelDimensionWindow(self)
477 def on_title_and_axes_labels_activate(self, widget, *args):
478 TitleAndAxesWindow(self)
480 def on_sections_activate(self, widget, *args):
481 if self.isColorImage:
482 self.statusBar.push(self.context,
483 'Function not available for color images.')
484 elif self.segments == []:
485 self.statusBar.push(self.context, 'No section to plot.')
486 elif self.sectionApp == None:
487 for index, color in enumerate(self.linescolors):
488 dprint (index, color, "cycle")
489 aSection = self.calculateSection(index)
490 print 'color', aSection.color
491 if self.sectionApp == None:
492 self.sectionApp = SectionApp(gladefile, aSection, self)
493 else:
494 self.sectionApp.addsection(aSection)
495 else:
496 self.sectionApp.window.show()
498 def on_sectionlist_activate(self, widget, *args):
499 colors = CircularList(len(self.colors))
500 colors.fromlist(self.colors)
501 text = ''
502 for n, seg in enumerate(self.segments):
503 (x1, y1), (x2, y2) = seg
504 lenAU = self.calculateDistance(seg, False)
505 text += '== Section '+ str(n+1) +' ('+ colors.get(n)+') ==\n'
506 text += 'x1 = %4d y1 = %4d\n' % (x1, y1)
507 text += 'x2 = %4d y2 = %4d\n' % (x2, y2)
508 text += 'Length: %5.2f, with pixel dimention (%2.2f x %2.2f).\n'\
509 % (lenAU, self.xPixelDim, self.yPixelDim)
510 text += '\n'
511 SectionListApp(text)
513 def on_clearsegments_activate(self, widget, *args):
514 if self.segments == []: return
515 if self.sectionApp != None:
516 self.sectionApp.destroy()
517 self.sectionApp = None
518 self.segments = []
519 self.linescolors = []
520 self.colorlist.resetIndex()
521 self.sectionsLC = None
522 #self.axim.clear()
523 self.plot_image()
525 def on_resetrange_activate(self, widget, *args):
526 self.plot_image(
527 min=int(self.image_array.min()),
528 max=int(self.image_array.max()))
530 def on_gaussianFilter_activate(self, widget, *args):
531 dprint ("Gaussian Filer")
532 GaussianFilterWindow(self)
534 def on_scrollingmean_activate(self, widget, *args):
535 if not self.isColorImage:
536 print 'Type:', self.image_array.dtype.name
537 self.image_array = self.image_array.astype('float32')
538 print 'Convolution spatial filter ... '
539 kernel = array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype='float32')
540 kernel /= kernel.sum()
541 print ' - Convolution kernel: \n', kernel
542 self.image_array = filters.convolve(self.image_array, kernel)
543 self.imagemode = 'floatarray'
544 self.plot_image()
545 print 'OK\n'
546 else:
547 self.statusBar.push(self.context,
548 "This mean can't be performed on color images.")
550 def on_reload_activate(self, widget, *args):
551 dprint ("Reloading image ...")
552 if self.filename != None:
553 self.on_clearsegments_activate(None, None)
554 self.load_image(self.filename)
557 class SectionListApp(GenericSecondaryWindow):
559 This class implement the text window used to show the sections' list.
561 def __init__(self, text):
562 GenericSecondaryWindow.__init__(self, 'SectionListWindow', gladefile)
564 textview = self.widgetTree.get_widget('sectionsTextView')
565 buffer = textview.get_buffer()
566 buffer.set_text(text)
568 def on_closeButton_clicked(self, widget, data=None):
569 self.window.destroy()
572 class GaussianFilterWindow(DialogWindowWithCancel):
574 Dialog for setting the gaussian filter options.
576 def __init__(self, callerApp):
577 DialogWindowWithCancel.__init__(self, 'GaussianFilterWindow',
578 gladefile, callerApp)
579 self.sigmaSpinButton = self.widgetTree.get_widget('sigmaSpinButton')
581 def on_okButton_clicked(self, widget, *args):
582 sigma = self.sigmaSpinButton.get_value()
583 dprint ("Ok, sigma =", sigma)
584 self.callerApp.image_array = filters.gaussian_filter(
585 self.callerApp.image_array, sigma)
586 self.callerApp.imagemode = 'floatarray'
587 self.callerApp.plot_image()
588 self.window.destroy()
591 class PixelDimensionWindow(DialogWindowWithCancel):
593 Dialog for setting the pixel dimension.
595 def __init__(self, callerApp):
596 DialogWindowWithCancel.__init__(self, 'PixelDimensionWindow',
597 gladefile, callerApp)
599 xDim = self.callerApp.xPixelDim
600 yDim = self.callerApp.yPixelDim
602 self.xPixDimSpinButt = self.widgetTree.get_widget('xPixDimSpinButt')
603 self.yPixDimSpinButt = self.widgetTree.get_widget('yPixDimSpinButt')
604 self.aspectSpinButt = self.widgetTree.get_widget('aspectSpinButt')
605 self.xPixDimSpinButt.set_value(xDim)
606 self.yPixDimSpinButt.set_value(yDim)
607 self.aspectSpinButt.set_value(self.callerApp.aspect)
609 def on_okButton_clicked(self, widget, *args):
610 dprint ("OK")
611 self.callerApp.xPixelDim = self.xPixDimSpinButt.get_value()
612 self.callerApp.yPixelDim = self.yPixDimSpinButt.get_value()
613 self.callerApp.aspect = self.aspectSpinButt.get_value()
614 self.callerApp.autoset_extent()
615 self.callerApp.plot_image()
616 self.window.destroy()
619 class TitleAndAxesWindow(DialogWindowWithCancel):
621 Dialog for setting Title and axes labels.
623 def __init__(self, callerApp):
624 DialogWindowWithCancel.__init__(self, 'TitleAndAxesWindow',
625 gladefile, callerApp)
627 self.titleEntry = self.widgetTree.get_widget('titleEntry')
628 self.xlabelEntry = self.widgetTree.get_widget('xlabelEntry')
629 self.ylabelEntry = self.widgetTree.get_widget('ylabelEntry')
631 def sync(field, entry):
632 if field != None: entry.set_text(field)
634 sync(self.callerApp.title, self.titleEntry)
635 sync(self.callerApp.xlabel, self.xlabelEntry)
636 sync(self.callerApp.ylabel, self.ylabelEntry)
638 def on_okButton_clicked(self, widget, *args):
639 dprint ("Ok clicked")
640 self.callerApp.axim.set_title(self.titleEntry.get_text())
641 self.callerApp.axim.set_xlabel(self.xlabelEntry.get_text())
642 self.callerApp.axim.set_ylabel(self.ylabelEntry.get_text())
643 self.callerApp.canvas.draw()
645 self.callerApp.title = self.titleEntry.get_text()
646 self.callerApp.xlabel = self.xlabelEntry.get_text()
647 self.callerApp.ylabel = self.ylabelEntry.get_text()
649 self.window.destroy()
652 class SaveFileDialog(DialogWindowWithCancel):
654 This class implements the "Save File" dialog.
656 def __init__(self, callerApp):
657 DialogWindowWithCancel.__init__(self, 'savefileDialog',
658 gladefile, callerApp)
660 def saveToSelectedFile(self):
661 filename = self.window.get_filename()
662 try:
663 if filename is not None:
664 filename += '.pickle'
665 f = open(filename, 'w')
666 cPickle.dump(self.callerApp.image_array, f)
667 self.callerApp.writeStatusBar('File "%s" saved.' % (filename))
668 else:
669 dprint(' WARNING: Save dialog returned no filename.')
670 except:
671 self.callerApp.writeStatusBar("Can't save file '%s'." % (filename))
672 self.window.hide()
674 def on_saveButton_clicked(self, widget, *args):
675 dprint ("save button clicked", self.window.get_filename())
676 self.saveToSelectedFile()
678 def on_openfileDialog_file_activated(self, widget, *args):
679 dprint ("Save File Dialog: file_activated", self.window.get_filename())
681 def on_openfileDialog_response(self, widget, *args):
682 dprint ("\nSave File Dialog: response event")
684 def on_cancelButton_clicked(self, widget, *args):
685 # Don't destroy the dialog so we remeber the folder next time
686 self.window.hide()
687 return True
689 class OpenFileDialog(DialogWindowWithCancel):
691 This class implements the "Open File" dialog.
693 def __init__(self, callerApp):
694 DialogWindowWithCancel.__init__(self, 'openfileDialog',
695 gladefile, callerApp)
697 def openSelectedFile(self):
698 filename = self.window.get_filename()
699 if filename != None:
700 self.window.hide()
701 self.callerApp.load_image(filename)
702 self.window.hide()
704 def on_openButton_clicked(self, widget, *args):
705 dprint ("open button clicked", self.window.get_filename())
706 self.openSelectedFile()
708 def on_openfileDialog_file_activated(self, widget, *args):
709 dprint ("Open File Dialog: file_activated", self.window.get_filename())
711 def on_openfileDialog_response(self, widget, *args):
712 dprint ("\nOpen File Dialog: response event")
714 def on_cancelButton_clicked(self, widget, *args):
715 # Don't destroy the dialog so we remeber the folder next time
716 self.window.hide()
717 return True
719 class AboutDialog(GenericSecondaryWindow):
721 Object for the "About Dialog".
723 def __init__(self):
724 GenericSecondaryWindow.__init__(self, 'aboutDialog', gladefile)
725 self.window.set_version(read_version(rootdir))
726 self.window.connect("response", lambda d, r: d.destroy())
728 def main():
729 file_name = sourcedir + 'samples/test-img.tif'
730 files = sys.argv[1:]
731 if len(files) < 1: files =(file_name,)
733 for filename in files:
734 try:
735 file = open(filename,'r')
736 ia = ImageApp(filename)
737 file.close()
738 except IOError:
739 print "\n File '"+filename+"' is not existent or not readable.\n"
740 ia = ImageApp()
741 ia.start()
743 if __name__ == "__main__": main()