3662b51ba2219151a76f4c47751b5178c3fc1996
[hooke.git] / hooke / hooke.py
1 #!/usr/bin/env python\r
2 \r
3 '''\r
4 HOOKE - A force spectroscopy review & analysis tool\r
5 \r
6 Copyright (C) 2008-2010 Massimo Sandal (University of Bologna, Italy).\r
7                         Rolf Schmidt (Concordia University, Canada).\r
8 \r
9 This program is released under the GNU General Public License version 2.\r
10 '''\r
11 \r
12 from libhooke import WX_GOOD\r
13 \r
14 import wxversion\r
15 wxversion.select(WX_GOOD)\r
16 import copy\r
17 import cStringIO\r
18 import os\r
19 import os.path\r
20 import sys\r
21 import glob\r
22 import time\r
23 \r
24 import imp\r
25 import wx\r
26 import wx.html\r
27 import wx.aui\r
28 import wxmpl\r
29 import wx.lib.agw.aui as aui\r
30 import wx.propgrid as wxpg\r
31 \r
32 import libhooke as lh\r
33 from config import config\r
34 import drivers\r
35 import plugins\r
36 import hookecommands\r
37 import hookeplaylist\r
38 import hookepropertyeditor\r
39 import hookeresults\r
40 import playlist\r
41 \r
42 global __version__\r
43 \r
44 __version__ = lh.HOOKE_VERSION[0]\r
45 __release_name__ = lh.HOOKE_VERSION[1]\r
46 \r
47 #TODO: order menu items, get rid of all unused IDs\r
48 ID_ExportText = wx.NewId()\r
49 ID_ExportImage = wx.NewId()\r
50 ID_Config = wx.NewId()\r
51 ID_About = wx.NewId()\r
52 ID_Next = wx.NewId()\r
53 ID_Previous = wx.NewId()\r
54 \r
55 ID_ViewAssistant = wx.NewId()\r
56 ID_ViewCommands = wx.NewId()\r
57 ID_ViewFolders = wx.NewId()\r
58 ID_ViewOutput = wx.NewId()\r
59 ID_ViewPlaylists = wx.NewId()\r
60 ID_ViewProperties = wx.NewId()\r
61 ID_ViewResults = wx.NewId()\r
62 \r
63 ID_CommandsList = wx.NewId()\r
64 ID_CommandsListBox = wx.NewId()\r
65 \r
66 ID_TextContent = wx.NewId()\r
67 ID_TreeContent = wx.NewId()\r
68 ID_HTMLContent = wx.NewId()\r
69 ID_SizeReportContent = wx.NewId()\r
70 ID_DeletePerspective = wx.NewId()\r
71 ID_SavePerspective = wx.NewId()\r
72 \r
73 ID_FirstPerspective = ID_SavePerspective + 1000\r
74 #I hope we'll never have more than 1000 perspectives\r
75 ID_FirstPlot = ID_SavePerspective + 2000\r
76 \r
77 class Hooke(wx.App):\r
78 \r
79     def OnInit(self):\r
80         self.SetAppName('Hooke')\r
81         self.SetVendorName('')\r
82 \r
83         #set the Hooke directory\r
84         lh.hookeDir = os.path.abspath(os.path.dirname(__file__))\r
85 \r
86         windowPosition = (config['main']['left'], config['main']['top'])\r
87         windowSize = (config['main']['width'], config['main']['height'])\r
88 \r
89         #setup the splashscreen\r
90         if config['splashscreen']['show']:\r
91             filename = lh.get_file_path('hooke.jpg', ['resources'])\r
92             if os.path.isfile(filename):\r
93                 bitmap = wx.Image(filename).ConvertToBitmap()\r
94                 splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT\r
95                 splashDuration = config['splashscreen']['duration']\r
96                 wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)\r
97                 wx.Yield()\r
98                 '''\r
99                 we need for the splash screen to disappear\r
100                 for whatever reason splashDuration and sleep do not correspond to each other\r
101                 at least not on Windows\r
102                 maybe it's because duration is in milliseconds and sleep in seconds\r
103                 thus we need to increase the sleep time a bit\r
104                 a factor of 1.2 seems to work quite well\r
105                 '''\r
106                 sleepFactor = 1.2\r
107                 time.sleep(sleepFactor * splashDuration / 1000)\r
108 \r
109         plugin_objects = []\r
110         for plugin in config['plugins']:\r
111             if config['plugins'][plugin]:\r
112                 filename = ''.join([plugin, '.py'])\r
113                 path = lh.get_file_path(filename, ['plugins'])\r
114                 if os.path.isfile(path):\r
115                     #get the corresponding filename and path\r
116                     plugin_name = ''.join(['plugins.', plugin])\r
117                     module = __import__(plugin_name)\r
118                     #get the file that contains the plugin\r
119                     class_file = getattr(plugins, plugin)\r
120                     #get the class that contains the commands\r
121                     class_object = getattr(class_file, plugin + 'Commands')\r
122                     plugin_objects.append(class_object)\r
123 \r
124         def make_command_class(*bases):\r
125             #create metaclass with plugins and plotmanipulators\r
126             return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})\r
127         frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=windowPosition, size=windowSize)\r
128         frame.Show(True)\r
129         self.SetTopWindow(frame)\r
130 \r
131         return True\r
132 \r
133     def OnExit(self):\r
134         #TODO: write values to ini file if necessary\r
135         return True\r
136 \r
137 \r
138 class HookeFrame(wx.Frame):\r
139 \r
140     def __init__(self, parent, id=-1, title='', pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE|wx.SUNKEN_BORDER|wx.CLIP_CHILDREN):\r
141         #call parent constructor\r
142         wx.Frame.__init__(self, parent, id, title, pos, size, style)\r
143         self.config = config\r
144         self.CreateApplicationIcon()\r
145         #self.configs contains: {the name of the Commands file: corresponding ConfigObj}\r
146         self.configs = {}\r
147         ##self.playlists contains: {the name of the playlist: [playlist, tabIndex, plotID]}\r
148         #self.playlists = {}\r
149         #self.plugins contains: {the name of the plugin: [caption, function]}\r
150         self.plugins = {}\r
151         #self.plotmanipulators list contains: [the name of the plotmanip, function, name of the module]\r
152         self.plotmanipulators = []\r
153 \r
154         #tell FrameManager to manage this frame\r
155         self._mgr = aui.AuiManager()\r
156         self._mgr.SetManagedWindow(self)\r
157         #set the gradient style\r
158         self._mgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_GRADIENT_TYPE, aui.AUI_GRADIENT_NONE)\r
159         #set transparent drag\r
160         self._mgr.SetFlags(self._mgr.GetFlags() ^ aui.AUI_MGR_TRANSPARENT_DRAG)\r
161 \r
162         # set up default notebook style\r
163         self._notebook_style = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER\r
164         self._notebook_theme = 0\r
165 \r
166         #holds the perspectives: {name, [index, perspectiveStr]}\r
167         self._perspectives = {}\r
168 \r
169         # min size for the frame itself isn't completely done.\r
170         # see the end up FrameManager::Update() for the test\r
171         # code. For now, just hard code a frame minimum size\r
172         self.SetMinSize(wx.Size(400, 300))\r
173         #create panels here\r
174         self.panelAssistant = self.CreatePanelAssistant()\r
175         self.panelCommands = self.CreatePanelCommands()\r
176         self.panelFolders = self.CreatePanelFolders()\r
177         self.panelPlaylists = self.CreatePanelPlaylists()\r
178         self.panelProperties = self.CreatePanelProperties()\r
179         self.panelOutput = self.CreatePanelOutput()\r
180         self.panelResults = self.CreatePanelResults()\r
181         self.plotNotebook = self.CreateNotebook()\r
182         #self.textCtrlCommandLine=self.CreateCommandLine()\r
183 \r
184         # add panes\r
185         self._mgr.AddPane(self.panelFolders, aui.AuiPaneInfo().Name('Folders').Caption('Folders').Left().CloseButton(True).MaximizeButton(False))\r
186         self._mgr.AddPane(self.panelPlaylists, aui.AuiPaneInfo().Name('Playlists').Caption('Playlists').Left().CloseButton(True).MaximizeButton(False))\r
187         self._mgr.AddPane(self.plotNotebook, aui.AuiPaneInfo().Name('Plots').CenterPane().PaneBorder(False))\r
188         self._mgr.AddPane(self.panelCommands, aui.AuiPaneInfo().Name('Commands').Caption('Settings and commands').Right().CloseButton(True).MaximizeButton(False))\r
189         self._mgr.AddPane(self.panelProperties, aui.AuiPaneInfo().Name('Properties').Caption('Properties').Right().CloseButton(True).MaximizeButton(False))\r
190         self._mgr.AddPane(self.panelAssistant, aui.AuiPaneInfo().Name('Assistant').Caption('Assistant').Right().CloseButton(True).MaximizeButton(False))\r
191         self._mgr.AddPane(self.panelOutput, aui.AuiPaneInfo().Name('Output').Caption('Output').Bottom().CloseButton(True).MaximizeButton(False))\r
192         self._mgr.AddPane(self.panelResults, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().CloseButton(True).MaximizeButton(False))\r
193         #self._mgr.AddPane(self.textCtrlCommandLine, aui.AuiPaneInfo().Name('CommandLine').CaptionVisible(False).Fixed().Bottom().Layer(2).CloseButton(False).MaximizeButton(False))\r
194         #self._mgr.AddPane(panelBottom, aui.AuiPaneInfo().Name("panelCommandLine").Bottom().Position(1).CloseButton(False).MaximizeButton(False))\r
195 \r
196         # add the toolbars to the manager\r
197         self.toolbar=self.CreateToolBar()\r
198         self.toolbarNavigation=self.CreateToolBarNavigation()\r
199         self._mgr.AddPane(self.toolbar, aui.AuiPaneInfo().Name('toolbar').Caption('Toolbar').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))\r
200         self._mgr.AddPane(self.toolbarNavigation, aui.AuiPaneInfo().Name('toolbarNavigation').Caption('Navigation').ToolbarPane().Top().Layer(1).Row(1).LeftDockable(False).RightDockable(False))\r
201         # "commit" all changes made to FrameManager\r
202         self._mgr.Update()\r
203         #create the menubar after the panes so that the default perspective\r
204         #is created with all panes open\r
205         self.CreateMenuBar()\r
206         self.statusbar = self.CreateStatusBar()\r
207         self._BindEvents()\r
208         #TODO: select item on startup (whatever item)\r
209         #self.listCtrlCommands.Select(0)\r
210         #self.OnListboxSelect(None)\r
211         name = self.config['perspectives']['active']\r
212         menu_item = self.GetPerspectiveMenuItem(name)\r
213         self.OnRestorePerspective(menu_item)\r
214         self.playlists = self.panelPlaylists.Playlists\r
215         #define the list of active drivers\r
216         self.drivers = []\r
217         for driver in self.config['drivers']:\r
218             if self.config['drivers'][driver]:\r
219                 #get the corresponding filename and path\r
220                 filename = ''.join([driver, '.py'])\r
221                 path = lh.get_file_path(filename, ['drivers'])\r
222                 #the driver is active for driver[1] == 1\r
223                 if os.path.isfile(path):\r
224                     #driver files are located in the 'drivers' subfolder\r
225                     driver_name = ''.join(['drivers.', driver])\r
226                     module = __import__(driver_name)\r
227                     class_file = getattr(drivers, driver)\r
228                     for command in dir(class_file):\r
229                         if command.endswith('Driver'):\r
230                             self.drivers.append(getattr(class_file, command))\r
231         #import all active plugins and plotmanips\r
232         #the plotmanip_functions contains: {the name of the plotmanip: [method, class_object]}\r
233         plotmanip_functions = {}\r
234         #add 'general.ini' to self.configs (this is not a plugin and thus must be imported seperately)\r
235         ini_path = lh.get_file_path('general.ini', ['plugins'])\r
236         plugin_config = ConfigObj(ini_path)\r
237         #self.config.merge(plugin_config)\r
238         self.configs['general'] = plugin_config\r
239         #make sure we execute _plug_init() for every command line plugin we import\r
240         for plugin in self.config['plugins']:\r
241             if self.config['plugins'][plugin]:\r
242                 filename = ''.join([plugin, '.py'])\r
243                 path = lh.get_file_path(filename, ['plugins'])\r
244                 if os.path.isfile(path):\r
245                     #get the corresponding filename and path\r
246                     plugin_name = ''.join(['plugins.', plugin])\r
247                     try:\r
248                         #import the module\r
249                         module = __import__(plugin_name)\r
250                         #prepare the ini file for inclusion\r
251                         ini_path = path.replace('.py', '.ini')\r
252                         #include ini file\r
253                         plugin_config = ConfigObj(ini_path)\r
254                         #self.config.merge(plugin_config)\r
255                         self.configs[plugin] = plugin_config\r
256                         #add to plugins\r
257                         commands = eval('dir(module.' + plugin+ '.' + plugin + 'Commands)')\r
258                         #keep only commands (ie names that start with 'do_')\r
259                         commands = [command for command in commands if command.startswith('do_')]\r
260                         if commands:\r
261                             self.plugins[plugin] = commands\r
262                         try:\r
263                             #initialize the plugin\r
264                             eval('module.' + plugin+ '.' + plugin + 'Commands._plug_init(self)')\r
265                         except AttributeError:\r
266                             pass\r
267                     except ImportError:\r
268                         pass\r
269         #initialize the commands tree\r
270         commands = dir(HookeFrame)\r
271         commands = [command for command in commands if command.startswith('do_')]\r
272         if commands:\r
273             self.plugins['general'] = commands\r
274         self.panelCommands.Initialize(self.plugins)\r
275         for command in dir(self):\r
276             if command.startswith('plotmanip_'):\r
277                 plotmanip_functions[command] = [command, getattr(self, command)]\r
278         for name in self.config['plotmanipulators']['names']:\r
279             if self.config['plotmanipulators'].as_bool(name):\r
280                 command_name = ''.join(['plotmanip_', name])\r
281                 if command_name in plotmanip_functions:\r
282                     self.plotmanipulators.append(plotmanip_functions[command_name])\r
283         #load default list, if possible\r
284         self.do_loadlist(self.config['general']['list'])\r
285 \r
286     def _BindEvents(self):\r
287         self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)\r
288         self.Bind(wx.EVT_SIZE, self.OnSize)\r
289         self.Bind(wx.EVT_CLOSE, self.OnClose)\r
290         # Show How To Use The Closing Panes Event\r
291         self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClose)\r
292         self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnNotebookPageClose)\r
293         #menu\r
294         self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)\r
295         self.Bind(wx.EVT_MENU, self.OnAbout, id=wx.ID_ABOUT)\r
296         #view\r
297         self.Bind(wx.EVT_MENU_RANGE, self.OnView, id=ID_ViewAssistant, id2=ID_ViewResults)\r
298         #perspectives\r
299         self.Bind(wx.EVT_MENU, self.OnDeletePerspective, id=ID_DeletePerspective)\r
300         self.Bind(wx.EVT_MENU, self.OnSavePerspective, id=ID_SavePerspective)\r
301         self.Bind(wx.EVT_MENU_RANGE, self.OnRestorePerspective, id=ID_FirstPerspective, id2=ID_FirstPerspective+1000)\r
302         #toolbar\r
303         self.Bind(wx.EVT_TOOL, self.OnExportImage, id=ID_ExportImage)\r
304         self.Bind(wx.EVT_TOOL, self.OnNext, id=ID_Next)\r
305         self.Bind(wx.EVT_TOOL, self.OnPrevious, id=ID_Previous)\r
306         #self.Bind(.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnDropDownToolbarItem, id=ID_DropDownToolbarItem)\r
307         #dir control\r
308         treeCtrl = self.panelFolders.GetTreeCtrl()\r
309         #tree.Bind(wx.EVT_LEFT_UP, self.OnDirCtrl1LeftUp)\r
310         #tree.Bind(wx.EVT_LEFT_DOWN, self.OnGenericDirCtrl1LeftDown)\r
311         treeCtrl.Bind(wx.EVT_LEFT_DCLICK, self.OnDirCtrlLeftDclick)\r
312         #playlist tree\r
313         self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DOWN, self.OnPlaylistsLeftDown)\r
314         self.panelPlaylists.PlaylistsTree.Bind(wx.EVT_LEFT_DCLICK, self.OnPlaylistsLeftDclick)\r
315         #commands tree\r
316         self.panelCommands.ExecuteButton.Bind(wx.EVT_BUTTON, self.OnExecute)\r
317         self.panelCommands.CommandsTree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeCtrlCommandsLeftDown)\r
318         #property editor\r
319         self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)\r
320         self.panelProperties.pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)\r
321         #results panel\r
322         self.panelResults.results_list.OnCheckItem = self.OnResultsCheck\r
323 \r
324     def _GetActiveCurveIndex(self):\r
325         playlist = self.GetActivePlaylist()\r
326         #get the selected item from the tree\r
327         selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
328         #test if a playlist or a curve was double-clicked\r
329         if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):\r
330             return -1\r
331         else:\r
332             count = 0\r
333             selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)\r
334             while selected_item.IsOk():\r
335                 count += 1\r
336                 selected_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)\r
337             return count\r
338 \r
339     def _GetActivePlaylistName(self):\r
340         #get the selected item from the tree\r
341         selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
342         #test if a playlist or a curve was double-clicked\r
343         if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):\r
344             playlist_item = selected_item\r
345         else:\r
346             #get the name of the playlist\r
347             playlist_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)\r
348         #now we have a playlist\r
349         return self.panelPlaylists.PlaylistsTree.GetItemText(playlist_item)\r
350 \r
351     def _GetPlaylistTab(self, name):\r
352         for index, page in enumerate(self.plotNotebook._tabs._pages):\r
353             if page.caption == name:\r
354                 return index\r
355         return -1\r
356 \r
357     def _GetUniquePlaylistName(self, name):\r
358         playlist_name = name\r
359         count = 1\r
360         while playlist_name in self.playlists:\r
361             playlist_name = ''.join([name, str(count)])\r
362             count += 1\r
363         return playlist_name\r
364 \r
365     def _SavePerspectiveToFile(self, name, perspective):\r
366         filename = ''.join([name, '.txt'])\r
367         filename = lh.get_file_path(filename, ['perspectives'])\r
368         perspectivesFile = open(filename, 'w')\r
369         perspectivesFile.write(perspective)\r
370         perspectivesFile.close()\r
371 \r
372     def AddPlaylist(self, playlist=None, name='Untitled'):\r
373         #TODO: change cursor or progressbar (maybe in statusbar)\r
374         #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
375         if playlist and playlist.count > 0:\r
376             playlist.name = self._GetUniquePlaylistName(name)\r
377             playlist.reset()\r
378             self.AddToPlaylists(playlist)\r
379 \r
380     def AddPlaylistFromFiles(self, files=[], name='Untitled'):\r
381         #TODO: change cursor or progressbar (maybe in statusbar)\r
382         #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
383         if files:\r
384             playlist = Playlist.Playlist(self.drivers)\r
385             for item in files:\r
386                 playlist.add_curve(item)\r
387         if playlist.count > 0:\r
388             playlist.name = self._GetUniquePlaylistName(name)\r
389             playlist.reset()\r
390             self.AddToPlaylists(playlist)\r
391 \r
392     def AddToPlaylists(self, playlist):\r
393         if playlist.has_curves:\r
394             #setup the playlist in the Playlist tree\r
395             tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()\r
396             playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)\r
397             #add all curves to the Playlist tree\r
398             curves = {}\r
399             for index, curve in enumerate(playlist.curves):\r
400                 ##remove the extension from the name of the curve\r
401                 ##TODO: optional?\r
402                 #item_text, extension = os.path.splitext(curve.name)\r
403                 #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)\r
404                 curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1)\r
405                 if index == playlist.index:\r
406                     self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID)\r
407             playlist.reset()\r
408             #create the plot tab and add playlist to the dictionary\r
409             plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists))\r
410             notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)\r
411             tab_index = self.plotNotebook.GetSelection()\r
412             figure = plotPanel.get_figure()\r
413             self.playlists[playlist.name] = [playlist, figure]\r
414             self.panelPlaylists.PlaylistsTree.Expand(playlist_root)\r
415             self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
416             self.UpdatePlot()\r
417 \r
418     def AppendToOutput(self, text):\r
419         self.panelOutput.AppendText(''.join([text, '\n']))\r
420 \r
421     def CreateApplicationIcon(self):\r
422         iconFile = 'resources' + os.sep + 'microscope.ico'\r
423         icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO)\r
424         self.SetIcon(icon)\r
425 \r
426     def CreateCommandLine(self):\r
427         return wx.TextCtrl(self, -1, '', style=wx.NO_BORDER|wx.EXPAND)\r
428 \r
429     def CreatePanelAssistant(self):\r
430         panel = wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)\r
431         panel.SetEditable(False)\r
432         return panel\r
433 \r
434     def CreatePanelCommands(self):\r
435         return hookecommands.Commands(self)\r
436 \r
437     def CreatePanelFolders(self):\r
438         #set file filters\r
439         filters = self.config['folders']['filters']\r
440         index = self.config['folders'].as_int('filterindex')\r
441         #set initial directory\r
442         folder = self.config['general']['workdir']\r
443         return wx.GenericDirCtrl(self, -1, dir=folder, size=(200, 250), style=wx.DIRCTRL_SHOW_FILTERS, filter=filters, defaultFilter=index)\r
444 \r
445     def CreatePanelOutput(self):\r
446         return wx.TextCtrl(self, -1, '', wx.Point(0, 0), wx.Size(150, 90), wx.NO_BORDER|wx.TE_MULTILINE)\r
447 \r
448     def CreatePanelPlaylists(self):\r
449         return hookeplaylist.Playlists(self)\r
450 \r
451     def CreatePanelProperties(self):\r
452         return hookepropertyeditor.PropertyEditor(self)\r
453 \r
454     def CreatePanelResults(self):\r
455         return hookeresults.Results(self)\r
456 \r
457     def CreatePanelWelcome(self):\r
458         ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300))\r
459         introStr = '<h1>Welcome to Hooke</h1>' + \\r
460                  '<h3>Features</h3>' + \\r
461                  '<ul>' + \\r
462                  '<li>View, annotate, measure force curves</li>' + \\r
463                  '<li>Worm-like chain fit of force peaks</li>' + \\r
464                  '<li>Automatic convolution-based filtering of empty curves</li>' + \\r
465                  '<li>Automatic fit and measurement of multiple force peaks</li>' + \\r
466                  '<li>Handles force-clamp force experiments (experimental)</li>' + \\r
467                  '<li>It is extensible by users by means of plugins and drivers</li>' + \\r
468                  '</ul>' + \\r
469                  '<p>See the <a href="/p/hooke/wiki/DocumentationIndex">DocumentationIndex</a> for more information</p>'\r
470         ctrl.SetPage(introStr)\r
471         return ctrl\r
472 \r
473     def CreateMenuBar(self):\r
474         menu_bar = wx.MenuBar()\r
475         #file\r
476         file_menu = wx.Menu()\r
477         file_menu.Append(wx.ID_OPEN, '&Open playlist\tCtrl-O')\r
478         file_menu.Append(wx.ID_SAVE, 'Save playlist\tCtrl-S')\r
479         file_menu.AppendSeparator()\r
480         file_menu.Append(wx.ID_EXIT, 'Exit\tCtrl-Q')\r
481         #edit\r
482         edit_menu = wx.Menu()\r
483         edit_menu.Append(ID_ExportText, 'Export text...')\r
484         edit_menu.Append(ID_ExportImage, 'Export image...')\r
485         edit_menu.AppendSeparator();\r
486         edit_menu.Append(ID_Config, 'Preferences')\r
487         #view\r
488         view_menu = wx.Menu()\r
489         view_menu.AppendCheckItem(ID_ViewFolders, 'Folders\tF5')\r
490         view_menu.AppendCheckItem(ID_ViewPlaylists, 'Playlists\tF6')\r
491         view_menu.AppendCheckItem(ID_ViewCommands, 'Commands\tF7')\r
492         view_menu.AppendCheckItem(ID_ViewProperties, 'Properties\tF8')\r
493         view_menu.AppendCheckItem(ID_ViewAssistant, 'Assistant\tF9')\r
494         view_menu.AppendCheckItem(ID_ViewResults, 'Results\tF10')\r
495         view_menu.AppendCheckItem(ID_ViewOutput, 'Output\tF11')\r
496         #perspectives\r
497         self._perspectives_menu = self.CreatePerspectivesMenu()\r
498         #help\r
499         help_menu = wx.Menu()\r
500         help_menu.Append(wx.ID_ABOUT, 'About Hooke')\r
501         #put it all together\r
502         menu_bar.Append(file_menu, 'File')\r
503         menu_bar.Append(edit_menu, 'Edit')\r
504         menu_bar.Append(view_menu, 'View')\r
505         menu_bar.Append(self._perspectives_menu, "Perspectives")\r
506         menu_bar.Append(help_menu, 'Help')\r
507 \r
508         self.SetMenuBar(menu_bar)\r
509 \r
510     def CreateNotebook(self):\r
511         # create the notebook off-window to avoid flicker\r
512         client_size = self.GetClientSize()\r
513         ctrl = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), self._notebook_style)\r
514         arts = [aui.AuiDefaultTabArt, aui.AuiSimpleTabArt, aui.VC71TabArt, aui.FF2TabArt, aui.VC8TabArt, aui.ChromeTabArt]\r
515         art = arts[self._notebook_theme]()\r
516         ctrl.SetArtProvider(art)\r
517         #uncomment if we find a nice icon\r
518         #page_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))\r
519         ctrl.AddPage(self.CreatePanelWelcome(), "Welcome", False)\r
520         return ctrl\r
521 \r
522     def CreatePerspectivesMenu(self):\r
523         menu = wx.Menu()\r
524         menu.Append(ID_SavePerspective, "Save Perspective")\r
525         menu.Append(ID_DeletePerspective, "Delete Perspective")\r
526         menu.AppendSeparator()\r
527         #add perspectives to menubar and _perspectives\r
528         perspectivesDirectory = os.path.join(lh.hookeDir, 'perspectives')\r
529         if os.path.isdir(perspectivesDirectory):\r
530             perspectiveFileNames = os.listdir(perspectivesDirectory)\r
531             for perspectiveFilename in perspectiveFileNames:\r
532                 filename = lh.get_file_path(perspectiveFilename, ['perspectives'])\r
533                 if os.path.isfile(filename):\r
534                     perspectiveFile = open(filename, 'rU')\r
535                     perspective = perspectiveFile.readline()\r
536                     perspectiveFile.close()\r
537                     if perspective != '':\r
538                         name, extension = os.path.splitext(perspectiveFilename)\r
539                         if extension == '.txt':\r
540                             menuItem = menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name)\r
541                             self._perspectives[name] = [len(self._perspectives), perspective]\r
542                             if self.config['perspectives']['active'] == name:\r
543                                 menuItem.Check()\r
544         #in case there are no perspectives\r
545         if not self._perspectives:\r
546             perspective = self._mgr.SavePerspective()\r
547             self.config['perspectives']['default'] = 'Default'\r
548             self._perspectives['Default'] = [0, perspective]\r
549             menuItem = menu.AppendRadioItem(ID_FirstPerspective, 'Default')\r
550             menuItem.Check()\r
551             self._SavePerspectiveToFile('Default', perspective)\r
552         return menu\r
553 \r
554     def CreateStatusbar(self):\r
555         statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)\r
556         statusbar.SetStatusWidths([-2, -3])\r
557         statusbar.SetStatusText('Ready', 0)\r
558         welcomeString=u'Welcome to Hooke (version '+__version__+', '+__release_name__+')!'\r
559         statusbar.SetStatusText(welcomeString, 1)\r
560         return statusbar\r
561 \r
562     def CreateToolBar(self):\r
563         toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)\r
564         toolbar.SetToolBitmapSize(wx.Size(16,16))\r
565         toolbar_bmp1 = wx.ArtProvider_GetBitmap(wx.ART_QUESTION, wx.ART_OTHER, wx.Size(16, 16))\r
566         toolbar_bmpOpen = wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, wx.Size(16, 16))\r
567         toolbar_bmpSave = wx.ArtProvider_GetBitmap(wx.ART_FILE_SAVE, wx.ART_OTHER, wx.Size(16, 16))\r
568         toolbar_bmpExportText = wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, wx.Size(16, 16))\r
569         toolbar_bmpExportImage = wx.ArtProvider_GetBitmap(wx.ART_MISSING_IMAGE, wx.ART_OTHER, wx.Size(16, 16))\r
570         toolbar.AddLabelTool(101, 'Open', toolbar_bmpOpen)\r
571         toolbar.AddLabelTool(102, 'Save', toolbar_bmpSave)\r
572         toolbar.AddSeparator()\r
573         toolbar.AddLabelTool(ID_ExportText, 'Export text...', toolbar_bmpExportText)\r
574         toolbar.AddLabelTool(ID_ExportImage, 'Export image...', toolbar_bmpExportImage)\r
575         toolbar.Realize()\r
576         return toolbar\r
577 \r
578     def CreateToolBarNavigation(self):\r
579         toolbar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER)\r
580         toolbar.SetToolBitmapSize(wx.Size(16,16))\r
581         toolbar_bmpBack = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, wx.ART_OTHER, wx.Size(16, 16))\r
582         toolbar_bmpForward = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, wx.ART_OTHER, wx.Size(16, 16))\r
583         toolbar.AddLabelTool(ID_Previous, 'Previous', toolbar_bmpBack, shortHelp='Previous curve')\r
584         toolbar.AddLabelTool(ID_Next, 'Next', toolbar_bmpForward, shortHelp='Next curve')\r
585         toolbar.Realize()\r
586         return toolbar\r
587 \r
588     def DeleteFromPlaylists(self, name):\r
589         if name in self.playlists:\r
590             del self.playlists[name]\r
591         tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()\r
592         item, cookie = self.panelPlaylists.PlaylistsTree.GetFirstChild(tree_root)\r
593         while item.IsOk():\r
594             playlist_name = self.panelPlaylists.PlaylistsTree.GetItemText(item)\r
595             if playlist_name == name:\r
596                 try:\r
597                     self.panelPlaylists.PlaylistsTree.Delete(item)\r
598                 except:\r
599                     pass\r
600             item = self.panelPlaylists.PlaylistsTree.GetNextSibling(item)\r
601         self.OnPlaylistsLeftDclick(None)\r
602 \r
603     def DeletePlotPage(self, name):\r
604         index = self._GetPlaylistTab(name)\r
605         plot = self.playlists[name][1]\r
606         plot = None\r
607         self.plotNotebook.RemovePage(index)\r
608         self.DeleteFromPlaylists(name)\r
609 \r
610     def GetActiveCurve(self):\r
611         playlist = self.GetActivePlaylist()\r
612         if playlist is not None:\r
613             return playlist.get_active_curve()\r
614         return None\r
615 \r
616     def GetActivePlaylist(self):\r
617         playlist_name = self._GetActivePlaylistName()\r
618         if playlist_name in self.playlists:\r
619             return self.playlists[playlist_name][0]\r
620         return None\r
621 \r
622     def GetActivePlot(self):\r
623         curve = self.GetActiveCurve()\r
624         if curve is not None:\r
625             return curve.plots[0]\r
626         return None\r
627 \r
628     def GetDockArt(self):\r
629         return self._mgr.GetArtProvider()\r
630 \r
631     def GetBoolFromConfig(self, *args):\r
632         if len(args) == 2:\r
633             plugin = args[0]\r
634             section = args[0]\r
635             key = args[1]\r
636         elif len(args) == 3:\r
637             plugin = args[0]\r
638             section = args[1]\r
639             key = args[2]\r
640         if self.configs.has_key(plugin):\r
641             config = self.configs[plugin]\r
642             return config[section][key].as_bool('value')\r
643         return None\r
644 \r
645     def GetFloatFromConfig(self, *args):\r
646         if len(args) == 2:\r
647             plugin = args[0]\r
648             section = args[0]\r
649             key = args[1]\r
650         elif len(args) == 3:\r
651             plugin = args[0]\r
652             section = args[1]\r
653             key = args[2]\r
654         if self.configs.has_key(plugin):\r
655             config = self.configs[plugin]\r
656             return config[section][key].as_float('value')\r
657         return None\r
658 \r
659     def GetIntFromConfig(self, *args):\r
660         if len(args) == 2:\r
661             plugin = args[0]\r
662             section = args[0]\r
663             key = args[1]\r
664         elif len(args) == 3:\r
665             plugin = args[0]\r
666             section = args[1]\r
667             key = args[2]\r
668         if self.configs.has_key(plugin):\r
669             config = self.configs[plugin]\r
670             return config[section][key].as_int('value')\r
671         return None\r
672 \r
673     def GetStringFromConfig(self, *args):\r
674         if len(args) == 2:\r
675             plugin = args[0]\r
676             section = args[0]\r
677             key = args[1]\r
678         elif len(args) == 3:\r
679             plugin = args[0]\r
680             section = args[1]\r
681             key = args[2]\r
682         if self.configs.has_key(plugin):\r
683             config = self.configs[plugin]\r
684             return config[section][key]['value']\r
685         return None\r
686 \r
687     def GetPerspectiveMenuItem(self, name):\r
688         index = self._perspectives[name][0]\r
689         perspective_Id = ID_FirstPerspective + index\r
690         menu_item = self.MenuBar.FindItemById(perspective_Id)\r
691         return menu_item\r
692 \r
693     def HasPlotmanipulator(self, name):\r
694         '''\r
695         returns True if the plotmanipulator 'name' is loaded, False otherwise\r
696         '''\r
697         for plotmanipulator in self.plotmanipulators:\r
698             if plotmanipulator[0] == name:\r
699                 return True\r
700         return False\r
701 \r
702     def OnAbout(self, event):\r
703         msg = 'Hooke\n\n'+\\r
704             'A free, open source data analysis platform\n'+\\r
705             '(c) 2006-2008 Massimo Sandal\n\n'+\\r
706             '(c) 2009 Dr. Rolf Schmidt\n\n'+\\r
707             'Released under the GNU GPL v2'\r
708         dialog = wx.MessageDialog(self, msg, "About Hooke", wx.OK | wx.ICON_INFORMATION)\r
709         dialog.ShowModal()\r
710         dialog.Destroy()\r
711 \r
712     def OnClose(self, event):\r
713         #apply changes\r
714         self.config['main']['height'] = str(self.GetSize().GetHeight())\r
715         self.config['main']['left'] = str(self.GetPosition()[0])\r
716         self.config['main']['top'] = str(self.GetPosition()[1])\r
717         self.config['main']['width'] = str(self.GetSize().GetWidth())\r
718         # Writing the configuration file to 'hooke.ini'\r
719         self.config.write()\r
720         self._mgr.UnInit()\r
721         del self._mgr\r
722         self.Destroy()\r
723 \r
724     def OnDeletePerspective(self, event):\r
725         pass\r
726 \r
727     def OnDirCtrlLeftDclick(self, event):\r
728         file_path = self.panelFolders.GetPath()\r
729         if os.path.isfile(file_path):\r
730             if file_path.endswith('.hkp'):\r
731                 self.do_loadlist(file_path)\r
732             else:\r
733                 pass\r
734         event.Skip()\r
735 \r
736     def OnEraseBackground(self, event):\r
737         event.Skip()\r
738 \r
739     def OnExecute(self, event):\r
740         item = self.panelCommands.CommandsTree.GetSelection()\r
741         if item.IsOk():\r
742             if self.panelCommands.CommandsTree.ItemHasChildren(item):\r
743                 pass\r
744             else:\r
745                 #get the plugin\r
746                 parent = self.panelCommands.CommandsTree.GetItemParent(item)\r
747             if not self.panelCommands.CommandsTree.ItemHasChildren(item):\r
748                 parent_text = self.panelCommands.CommandsTree.GetItemText(parent)\r
749                 item_text = self.panelCommands.CommandsTree.GetItemText(item)\r
750                 if item_text in ['genlist', 'loadlist', 'savelist']:\r
751                     property_values = self.panelProperties.GetPropertyValues()\r
752                     arg_str = ''\r
753                     for item in property_values:\r
754                         arg_str = ''.join([arg_str, item, '=r"', str(property_values[item]), '", '])\r
755                     command = ''.join(['self.do_', item_text, '(', arg_str, ')'])\r
756                 else:\r
757                     command = ''.join(['self.do_', item_text, '()'])\r
758                 exec(command)\r
759         pass\r
760 \r
761     def OnExit(self, event):\r
762         self.Close()\r
763 \r
764     def OnExportImage(self, event):\r
765         pass\r
766 \r
767     def OnNext(self, event):\r
768         '''\r
769         NEXT\r
770         Go to the next curve in the playlist.\r
771         If we are at the last curve, we come back to the first.\r
772         -----\r
773         Syntax: next, n\r
774         '''\r
775         selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
776         if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):\r
777             #GetFirstChild returns a tuple\r
778             #we only need the first element\r
779             next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(selected_item)[0]\r
780         else:\r
781             next_item = self.panelPlaylists.PlaylistsTree.GetNextSibling(selected_item)\r
782             if not next_item.IsOk():\r
783                 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)\r
784                 #GetFirstChild returns a tuple\r
785                 #we only need the first element\r
786                 next_item = self.panelPlaylists.PlaylistsTree.GetFirstChild(parent_item)[0]\r
787         self.panelPlaylists.PlaylistsTree.SelectItem(next_item, True)\r
788         playlist = self.playlists[self._GetActivePlaylistName()][0]\r
789         if playlist.count > 1:\r
790             playlist.next()\r
791             self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
792             self.UpdatePlot()\r
793 \r
794     def OnNotebookPageClose(self, event):\r
795         ctrl = event.GetEventObject()\r
796         playlist_name = ctrl.GetPageText(ctrl._curpage)\r
797         self.DeleteFromPlaylists(playlist_name)\r
798 \r
799     def OnPaneClose(self, event):\r
800         event.Skip()\r
801 \r
802     def OnPlaylistsLeftDclick(self, event):\r
803         playlist_name = self._GetActivePlaylistName()\r
804         #if that playlist already exists\r
805         #we check if it is the active playlist (ie selected in panelPlaylists)\r
806         #and switch to it if necessary\r
807         if playlist_name in self.playlists:\r
808             index = self.plotNotebook.GetSelection()\r
809             current_playlist = self.plotNotebook.GetPageText(index)\r
810             #new_playlist = self.playlists[playlist_name][0]\r
811             #if current_playlist != new_playlist:\r
812             if current_playlist != playlist_name:\r
813                 index = self._GetPlaylistTab(playlist_name)\r
814                 self.plotNotebook.SetSelection(index)\r
815             #if a curve was double-clicked\r
816             item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
817             #TODO: fix with get_active_curve\r
818             if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):\r
819                 index = self._GetActiveCurveIndex()\r
820             else:\r
821                 index = 0\r
822             if index >= 0:\r
823                 playlist = self.playlists[playlist_name][0]\r
824                 playlist.index = index\r
825                 self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
826                 self.UpdatePlot()\r
827         #if you uncomment the following line, the tree will collapse/expand as well\r
828         #event.Skip()\r
829 \r
830     def OnPlaylistsLeftDown(self, event):\r
831         hit_item, hit_flags = self.panelPlaylists.PlaylistsTree.HitTest(event.GetPosition())\r
832         if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:\r
833             #self.SetFocus()\r
834             self.panelPlaylists.PlaylistsTree.SelectItem(hit_item)\r
835             playlist_name = self._GetActivePlaylistName()\r
836             playlist = self.playlists[playlist_name][0]\r
837             #if a curve was clicked\r
838             item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
839             if not self.panelPlaylists.PlaylistsTree.ItemHasChildren(item):\r
840                 #TODO: fix with get_active_curve\r
841                 index = self._GetActiveCurveIndex()\r
842                 if index >= 0:\r
843                     #playlist = self.playlists[playlist_name][0]\r
844                     playlist.index = index\r
845                     #self.playlists[playlist_name][0].index = index\r
846             #else:\r
847                 ##self.playlists[playlist_name][0].index = 0\r
848                 #playlist.index = index\r
849             self.playlists[playlist_name][0] = playlist\r
850         event.Skip()\r
851 \r
852     def OnPrevious(self, event):\r
853         '''\r
854         PREVIOUS\r
855         Go to the previous curve in the playlist.\r
856         If we are at the first curve, we jump to the last.\r
857         -------\r
858         Syntax: previous, p\r
859         '''\r
860         #playlist = self.playlists[self._GetActivePlaylistName()][0]\r
861         #select the previous curve and tell the user if we wrapped around\r
862         #self.AppendToOutput(playlist.previous())\r
863         selected_item = self.panelPlaylists.PlaylistsTree.GetSelection()\r
864         if self.panelPlaylists.PlaylistsTree.ItemHasChildren(selected_item):\r
865             previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(selected_item)\r
866         else:\r
867             previous_item = self.panelPlaylists.PlaylistsTree.GetPrevSibling(selected_item)\r
868             if not previous_item.IsOk():\r
869                 parent_item = self.panelPlaylists.PlaylistsTree.GetItemParent(selected_item)\r
870                 previous_item = self.panelPlaylists.PlaylistsTree.GetLastChild(parent_item)\r
871         self.panelPlaylists.PlaylistsTree.SelectItem(previous_item, True)\r
872         playlist = self.playlists[self._GetActivePlaylistName()][0]\r
873         if playlist.count > 1:\r
874             playlist.previous()\r
875             self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
876             self.UpdatePlot()\r
877 \r
878     def OnPropGridChanged (self, event):\r
879         prop = event.GetProperty()\r
880         if prop:\r
881             item_section = self.panelProperties.SelectedTreeItem\r
882             item_plugin = self.panelCommands.CommandsTree.GetItemParent(item_section)\r
883             plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)\r
884             config = self.configs[plugin]\r
885             property_section = self.panelCommands.CommandsTree.GetItemText(item_section)\r
886             property_key = prop.GetName()\r
887             property_value = prop.GetValue()\r
888             config[property_section][property_key]['value'] = property_value\r
889 \r
890     def OnPropGridSelect(self, event):\r
891         pass\r
892 \r
893     def OnRestorePerspective(self, event):\r
894         name = self.MenuBar.FindItemById(event.GetId()).GetLabel()\r
895         self._mgr.LoadPerspective(self._perspectives[name][1])\r
896         self.config['perspectives']['active'] = name\r
897         self._mgr.Update()\r
898         all_panes = self._mgr.GetAllPanes()\r
899         for pane in all_panes:\r
900             if not pane.name.startswith('toolbar'):\r
901                 if pane.name == 'Assistant':\r
902                     self.MenuBar.FindItemById(ID_ViewAssistant).Check(pane.window.IsShown())\r
903                 if pane.name == 'Folders':\r
904                     self.MenuBar.FindItemById(ID_ViewFolders).Check(pane.window.IsShown())\r
905                 if pane.name == 'Playlists':\r
906                     self.MenuBar.FindItemById(ID_ViewPlaylists).Check(pane.window.IsShown())\r
907                 if pane.name == 'Commands':\r
908                     self.MenuBar.FindItemById(ID_ViewCommands).Check(pane.window.IsShown())\r
909                 if pane.name == 'Properties':\r
910                     self.MenuBar.FindItemById(ID_ViewProperties).Check(pane.window.IsShown())\r
911                 if pane.name == 'Output':\r
912                     self.MenuBar.FindItemById(ID_ViewOutput).Check(pane.window.IsShown())\r
913                 if pane.name == 'Results':\r
914                     self.MenuBar.FindItemById(ID_ViewResults).Check(pane.window.IsShown())\r
915 \r
916     def OnResultsCheck(self, index, flag):\r
917         curve = self.GetActiveCurve()\r
918         result = curve.data['results'][index]['visible'] = flag\r
919         self.UpdatePlot()\r
920 \r
921     def OnSavePerspective(self, event):\r
922         def nameExists(name):\r
923             for item in self._perspectives_menu.GetMenuItems():\r
924                 if item.GetText() == name:\r
925                     return True\r
926             return False\r
927 \r
928         done = False\r
929         while not done:\r
930             dialog = wx.TextEntryDialog(self, 'Enter a name for the new perspective:', 'Save perspective')\r
931             dialog.SetValue('New perspective')\r
932             if dialog.ShowModal() != wx.ID_OK:\r
933                 return\r
934             else:\r
935                 name = dialog.GetValue()\r
936 \r
937             if nameExists(name):\r
938                 dialogConfirm = wx.MessageDialog(self, 'A file with this name already exists.\n\nDo you want to replace it?', 'Confirm', wx.YES_NO|wx.ICON_QUESTION|wx.CENTER)\r
939                 if dialogConfirm.ShowModal() == wx.ID_YES:\r
940                     done = True\r
941             else:\r
942                 done = True\r
943 \r
944         perspective = self._mgr.SavePerspective()\r
945 \r
946         if nameExists(name):\r
947             #check the corresponding menu item\r
948             menuItem = self.GetPerspectiveMenuItem(name)\r
949             #replace the perspectiveStr in _pespectives\r
950             index = self._perspectives[name][0]\r
951             self._perspectives[name] = [index, perspective]\r
952         else:\r
953             #simply add the new perspective to _perspectives\r
954             index = len(self._perspectives)\r
955             self._perspectives[name] = [len(self._perspectives), perspective]\r
956             menuItem = self._perspectives_menu.AppendRadioItem(ID_FirstPerspective + len(self._perspectives), name)\r
957 \r
958         menuItem.Check()\r
959         self._SavePerspectiveToFile(name, perspective)\r
960         #uncheck all perspective menu items\r
961         #as these are radio items, one item has to be checked at all times\r
962         #the line 'menuItem.Check()' above actually checks a second item\r
963         #but does not toggle\r
964         #so we need to uncheck all other items afterwards\r
965         #weirdly enough, menuitem.Toggle() doesn't do this properly either\r
966         for item in self._perspectives_menu.GetMenuItems():\r
967             if item.IsCheckable():\r
968                 if item.GetLabel() != name:\r
969                     item.Check(False)\r
970 \r
971     def OnView(self, event):\r
972         menu_id = event.GetId()\r
973         menu_item = self.MenuBar.FindItemById(menu_id)\r
974         menu_label = menu_item.GetLabel()\r
975 \r
976         pane = self._mgr.GetPane(menu_label)\r
977         pane.Show(not pane.IsShown())\r
978         #if we don't do the following, the Folders pane does not resize properly on hide/show\r
979         if pane.caption == 'Folders' and pane.IsShown() and pane.IsDocked():\r
980             #folders_size = pane.GetSize()\r
981             self.panelFolders.Fit()\r
982         self._mgr.Update()\r
983 \r
984     def OnSize(self, event):\r
985         event.Skip()\r
986 \r
987     def OnTreeCtrlCommandsLeftDown(self, event):\r
988         hit_item, hit_flags = self.panelCommands.CommandsTree.HitTest(event.GetPosition())\r
989         if (hit_flags & wx.TREE_HITTEST_ONITEM) != 0:\r
990             self.panelCommands.CommandsTree.SelectItem(hit_item)\r
991             self.panelProperties.SelectedTreeItem = hit_item\r
992             #if a command was clicked\r
993             properties = []\r
994             if not self.panelCommands.CommandsTree.ItemHasChildren(hit_item):\r
995                 item_plugin = self.panelCommands.CommandsTree.GetItemParent(hit_item)\r
996                 plugin = self.panelCommands.CommandsTree.GetItemText(item_plugin)\r
997                 if self.configs.has_key(plugin):\r
998                     #config = self.panelCommands.CommandsTree.GetPyData(item_plugin)\r
999                     config = self.configs[plugin]\r
1000                     section = self.panelCommands.CommandsTree.GetItemText(hit_item)\r
1001                     #display docstring in help window\r
1002                     doc_string = eval('self.do_' + section + '.__doc__')\r
1003                     if section in config:\r
1004                         for option in config[section]:\r
1005                             properties.append([option, config[section][option]])\r
1006             else:\r
1007                 module = self.panelCommands.CommandsTree.GetItemText(hit_item)\r
1008                 if module != 'general':\r
1009                     doc_string = eval('plugins.' + module + '.' + module + 'Commands.__doc__')\r
1010                 else:\r
1011                     doc_string = 'The module "general" contains Hooke core functionality'\r
1012             if doc_string is not None:\r
1013                 self.panelAssistant.ChangeValue(doc_string)\r
1014             hookepropertyeditor.PropertyEditor.Initialize(self.panelProperties, properties)\r
1015         event.Skip()\r
1016 \r
1017     def UpdatePlaylists(self):\r
1018         #setup the playlist in the Playlist tree\r
1019         tree_root = self.panelPlaylists.PlaylistsTree.GetRootItem()\r
1020         playlist_root = self.panelPlaylists.PlaylistsTree.AppendItem(tree_root, playlist.name, 0)\r
1021         #add all curves to the Playlist tree\r
1022         curves = {}\r
1023         for index, curve in enumerate(playlist.curves):\r
1024             ##remove the extension from the name of the curve\r
1025             ##TODO: optional?\r
1026             #item_text, extension = os.path.splitext(curve.name)\r
1027             #curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, item_text, 1)\r
1028             curve_ID = self.panelPlaylists.PlaylistsTree.AppendItem(playlist_root, curve.name, 1)\r
1029             if index == playlist.index:\r
1030                 self.panelPlaylists.PlaylistsTree.SelectItem(curve_ID)\r
1031         #create the plot tab and add playlist to the dictionary\r
1032         plotPanel = wxmpl.PlotPanel(self, ID_FirstPlot + len(self.playlists))\r
1033         notebook_tab = self.plotNotebook.AddPage(plotPanel, playlist.name, True)\r
1034         #tab_index = self.plotNotebook.GetSelection()\r
1035         figure = plotPanel.get_figure()\r
1036         #self.playlists[playlist.name] = [playlist, tab_index, figure]\r
1037         self.playlists[playlist.name] = [playlist, figure]\r
1038         self.panelPlaylists.PlaylistsTree.Expand(playlist_root)\r
1039         self.statusbar.SetStatusText(playlist.get_status_string(), 0)\r
1040         self.UpdatePlot()\r
1041 \r
1042 #HELPER FUNCTIONS\r
1043 #Everything sending an event should be here\r
1044     def _measure_N_points(self, N, whatset=1):\r
1045         '''\r
1046         general helper function for N-points measures\r
1047         '''\r
1048         wx.PostEvent(self.frame,self.list_of_events['measure_points'](num_of_points=N, set=whatset))\r
1049         while 1:\r
1050             try:\r
1051                 points=self.frame.events_from_gui.get()\r
1052                 break\r
1053             except Empty:\r
1054                 pass\r
1055         return points\r
1056 \r
1057     def _clickize(self, xvector, yvector, index):\r
1058         '''\r
1059         returns a ClickedPoint() object from an index and vectors of x, y coordinates\r
1060         '''\r
1061         point = lh.ClickedPoint()\r
1062         point.index = index\r
1063         point.absolute_coords = xvector[index], yvector[index]\r
1064         point.find_graph_coords(xvector, yvector)\r
1065         return point\r
1066 \r
1067 #PLAYLIST INTERACTION COMMANDS\r
1068 #-------------------------------\r
1069     def do_genlist(self, folder=lh.hookeDir, filemask='*.*'):\r
1070         '''\r
1071         GENLIST\r
1072         Generates a file playlist.\r
1073         Note it doesn't *save* it: see savelist for this.\r
1074 \r
1075         If [input files] is a directory, it will use all files in the directory for playlist.\r
1076         So:\r
1077         genlist dir\r
1078         genlist dir/\r
1079         genlist dir/*.*\r
1080 \r
1081         are all equivalent syntax.\r
1082         ------------\r
1083         Syntax: genlist [input files]\r
1084         '''\r
1085         #args list is: input folder, file mask\r
1086         if os.path.isdir(folder):\r
1087             path = os.path.join(folder, filemask)\r
1088             #expanding correctly the input list with the glob module :)\r
1089             files = glob.glob(path)\r
1090             files.sort()\r
1091             #TODO: change cursor or progressbar (maybe in statusbar)\r
1092             #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
1093             playlist = playlist.Playlist(self.drivers)\r
1094             for item in files:\r
1095                 curve = playlist.add_curve(item)\r
1096                 plot = copy.deepcopy(curve.plots[0])\r
1097                 #add the 'raw' data\r
1098                 curve.add_data('raw', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')\r
1099                 curve.add_data('raw', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')\r
1100                 #apply all active plotmanipulators and add the 'manipulated' data\r
1101                 for plotmanipulator in self.plotmanipulators:\r
1102                     plot = plotmanipulator[1](plot, curve)\r
1103                     curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')\r
1104                     curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')\r
1105             if playlist.count > 0:\r
1106                 playlist.name = self._GetUniquePlaylistName(os.path.basename(folder))\r
1107                 playlist.reset()\r
1108                 self.AddToPlaylists(playlist)\r
1109             self.AppendToOutput(playlist.get_status_string())\r
1110         else:\r
1111             self.AppendToOutput(''.join(['Cannot find folder ', folder]))\r
1112 \r
1113     def do_loadlist(self, filename):\r
1114         '''\r
1115         LOADLIST\r
1116         Loads a file playlist\r
1117         -----------\r
1118         Syntax: loadlist [playlist file]\r
1119         '''\r
1120         #TODO: check for duplicate playlists, ask the user for a unique name\r
1121         #if self.playlist_name in self.playlists:\r
1122 \r
1123         #add hkp extension if necessary\r
1124         if not filename.endswith('.hkp'):\r
1125             filename = ''.join([filename, '.hkp'])\r
1126         #prefix with 'hookeDir' if just a filename or a relative path\r
1127         filename = lh.get_file_path(filename)\r
1128         if os.path.isfile(filename):\r
1129             #TODO: change cursor\r
1130             #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
1131             playlist_new = playlist.Playlist(self.drivers)\r
1132             playlist_new.load(filename)\r
1133             if playlist_new.count > 0:\r
1134                 for curve in playlist_new.curves:\r
1135                     plot = copy.deepcopy(curve.plots[0])\r
1136                     for plotmanip in self.plotmanipulators:\r
1137                         #to_plot = plotmanip[1](to_plot, curve)\r
1138                         plot = plotmanip[1](plot, curve)\r
1139                         curve.set_data('manipulated', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')\r
1140                         curve.add_data('manipulated', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')\r
1141                 self.AddToPlaylists(playlist_new)\r
1142             #else:\r
1143                 ##TODO: display dialog\r
1144             self.AppendToOutput(playlist_new.get_status_string())\r
1145             #TODO: change cursor\r
1146             #self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))\r
1147         else:\r
1148             #TODO: display dialog\r
1149             self.AppendToOutput(''.join['File ', filename, ' not found.\n'])\r
1150         pass\r
1151 \r
1152     def do_savelist(self, filename):\r
1153         '''\r
1154         SAVELIST\r
1155         Saves the current file playlist on disk.\r
1156         ------------\r
1157         Syntax: savelist [filename]\r
1158         '''\r
1159 \r
1160         #self.playlist_generics['pointer'] = self._GetActiveCurveIndex\r
1161         pointer = self._GetActiveCurveIndex()\r
1162         #autocomplete filename if not specified\r
1163         if not filename.endswith('.hkp'):\r
1164             filename = filename.join(['.hkp'])\r
1165 \r
1166         playlist = self.GetActivePlaylist()\r
1167         playlist.set_XML()\r
1168         playlist.save(filename)\r
1169 \r
1170 #PLOT INTERACTION COMMANDS\r
1171 #-------------------------------\r
1172     def UpdatePlot(self):\r
1173         def add_plot(plot):\r
1174             if plot['visible'] and plot['x'] and plot['y']:\r
1175                 color = plot['color']\r
1176                 style = plot['style']\r
1177                 if style == 'plot':\r
1178                     axes.plot(plot['x'], plot['y'], color=color, zorder=1)\r
1179                 if style == 'scatter':\r
1180                     axes.scatter(plot['x'], plot['y'], color=color, s=0.5, zorder=2)\r
1181 \r
1182         def add_plot2(plot):\r
1183             if plot.visible and plot.x and plot.y:\r
1184                 if plot.style == 'plot':\r
1185                     axes.plot(plot.x, plot.y, color=plot.color, zorder=1)\r
1186                 if plot.style == 'scatter':\r
1187                     axes.scatter(plot.x, plot.y, color=plot.color, s=0.5, zorder=2)\r
1188 \r
1189         playlist_name = self._GetActivePlaylistName()\r
1190         index = self._GetActiveCurveIndex()\r
1191         playlist = self.playlists[playlist_name][0]\r
1192         curve = playlist.get_active_curve()\r
1193         plot = playlist.get_active_plot()\r
1194         figure = self.playlists[playlist_name][1]\r
1195 \r
1196         figure.clf()\r
1197         exclude = None\r
1198         if curve.data.has_key('manipulated'):\r
1199             exclude = 'raw'\r
1200         elif curve.data.has_key('raw'):\r
1201             exclude = 'manipulated'\r
1202 \r
1203         if exclude is not None:\r
1204             #TODO: what is this good for?\r
1205             if not hasattr(self, 'subplot'):\r
1206                 axes = figure.add_subplot(111)\r
1207 \r
1208             axes.set_title(plot.title)\r
1209             axes.set_xlabel(plot.units[0])\r
1210             axes.set_ylabel(plot.units[1])\r
1211 \r
1212             for set_of_plots in curve.data:\r
1213                 if set_of_plots != exclude:\r
1214                     plots = curve.data[set_of_plots]\r
1215                     for each_plot in plots:\r
1216                         add_plot(each_plot)\r
1217 \r
1218             #TODO: add multiple results support\r
1219             #for fit in curve.fits:\r
1220             if curve.fits.has_key('wlc'):\r
1221                 for plot in curve.fits['wlc'].results:\r
1222                     add_plot2(plot)\r
1223                 self.panelResults.DisplayResults(curve.fits['wlc'])\r
1224             else:\r
1225                 self.panelResults.ClearResults()\r
1226 \r
1227             axes.figure.canvas.draw()\r
1228         else:\r
1229             self.AppendToOutput('Not able to plot.')\r
1230 \r
1231 \r
1232 ID_PaneBorderSize = wx.ID_HIGHEST + 1\r
1233 ID_SashSize = ID_PaneBorderSize + 1\r
1234 ID_CaptionSize = ID_PaneBorderSize + 2\r
1235 ID_BackgroundColor = ID_PaneBorderSize + 3\r
1236 ID_SashColor = ID_PaneBorderSize + 4\r
1237 ID_InactiveCaptionColor =  ID_PaneBorderSize + 5\r
1238 ID_InactiveCaptionGradientColor = ID_PaneBorderSize + 6\r
1239 ID_InactiveCaptionTextColor = ID_PaneBorderSize + 7\r
1240 ID_ActiveCaptionColor = ID_PaneBorderSize + 8\r
1241 ID_ActiveCaptionGradientColor = ID_PaneBorderSize + 9\r
1242 ID_ActiveCaptionTextColor = ID_PaneBorderSize + 10\r
1243 ID_BorderColor = ID_PaneBorderSize + 11\r
1244 ID_GripperColor = ID_PaneBorderSize + 12\r
1245 \r
1246 \r
1247 #----------------------------------------------------------------------\r
1248 \r
1249 if __name__ == '__main__':\r
1250 \r
1251     ## now, silence a deprecation warning for py2.3\r
1252     #import warnings\r
1253     #warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")\r
1254 \r
1255     redirect=True\r
1256     if __debug__:\r
1257         redirect=False\r
1258     app = Hooke(redirect=redirect)\r
1259 \r
1260     app.MainLoop()\r
1261 \r
1262 \r