--- /dev/null
+# -*- coding: utf-8 -*-
+
+# An ordered dictionary implementation. OrderedDict entered the
+# Python Standard Library in Python 2.7 and 3.1. See `PEP 372`_ for
+# details. Code_ by Armin Ronacher and the PEP 273 authors.
+#
+# .. _PEP 372: http://www.python.org/dev/peps/pep-0372/
+# .. _Code: http://dev.pocoo.org/hg/sandbox/raw-file/tip/odict.py
+
+"""
+ odict
+ ~~~~~
+
+ This module is an example implementation of an ordered dict for the
+ collections module. It's not written for performance (it actually
+ performs pretty bad) but to show how the API works.
+
+
+ Questions and Answers
+ =====================
+
+ Why would anyone need ordered dicts?
+
+ Dicts in python are unordered which means that the order of items when
+ iterating over dicts is undefined. As a matter of fact it is most of
+ the time useless and differs from implementation to implementation.
+
+ Many developers stumble upon that problem sooner or later when
+ comparing the output of doctests which often does not match the order
+ the developer thought it would.
+
+ Also XML systems such as Genshi have their problems with unordered
+ dicts as the input and output ordering of tag attributes is often
+ mixed up because the ordering is lost when converting the data into
+ a dict. Switching to lists is often not possible because the
+ complexity of a lookup is too high.
+
+ Another very common case is metaprogramming. The default namespace
+ of a class in python is a dict. With Python 3 it becomes possible
+ to replace it with a different object which could be an ordered dict.
+ Django is already doing something similar with a hack that assigns
+ numbers to some descriptors initialized in the class body of a
+ specific subclass to restore the ordering after class creation.
+
+ When porting code from programming languages such as PHP and Ruby
+ where the item-order in a dict is guaranteed it's also a great help
+ to have an equivalent data structure in Python to ease the transition.
+
+ Where are new keys added?
+
+ At the end. This behavior is consistent with Ruby 1.9 Hashmaps
+ and PHP Arrays. It also matches what common ordered dict
+ implementations do currently.
+
+ What happens if an existing key is reassigned?
+
+ The key is *not* moved. This is consitent with existing
+ implementations and can be changed by a subclass very easily::
+
+ class movingodict(odict):
+ def __setitem__(self, key, value):
+ self.pop(key, None)
+ odict.__setitem__(self, key, value)
+
+ Moving keys to the end of a ordered dict on reassignment is not
+ very useful for most applications.
+
+ Does it mean the dict keys are sorted by a sort expression?
+
+ That's not the case. The odict only guarantees that there is an order
+ and that newly inserted keys are inserted at the end of the dict. If
+ you want to sort it you can do so, but newly added keys are again added
+ at the end of the dict.
+
+ I initializes the odict with a dict literal but the keys are not
+ ordered like they should!
+
+ Dict literals in Python generate dict objects and as such the order of
+ their items is not guaranteed. Before they are passed to the odict
+ constructor they are already unordered.
+
+ What happens if keys appear multiple times in the list passed to the
+ constructor?
+
+ The same as for the dict. The latter item overrides the former. This
+ has the side-effect that the position of the first key is used because
+ the key is actually overwritten:
+
+ >>> odict([('a', 1), ('b', 2), ('a', 3)])
+ odict.odict([('a', 3), ('b', 2)])
+
+ This behavor is consistent with existing implementation in Python
+ and the PHP array and the hashmap in Ruby 1.9.
+
+ This odict doesn't scale!
+
+ Yes it doesn't. The delitem operation is O(n). This is file is a
+ mockup of a real odict that could be implemented for collections
+ based on an linked list.
+
+ Why is there no .insert()?
+
+ There are few situations where you really want to insert a key at
+ an specified index. To now make the API too complex the proposed
+ solution for this situation is creating a list of items, manipulating
+ that and converting it back into an odict:
+
+ >>> d = odict([('a', 42), ('b', 23), ('c', 19)])
+ >>> l = d.items()
+ >>> l.insert(1, ('x', 0))
+ >>> odict(l)
+ odict.odict([('a', 42), ('x', 0), ('b', 23), ('c', 19)])
+
+ :copyright: (c) 2008 by Armin Ronacher and PEP 273 authors.
+ :license: modified BSD license.
+"""
+from itertools import izip, imap
+from copy import deepcopy
+
+missing = object()
+
+
+class odict(dict):
+ """
+ Ordered dict example implementation.
+
+ This is the proposed interface for a an ordered dict as proposed on the
+ Python mailinglist (proposal_).
+
+ It's a dict subclass and provides some list functions. The implementation
+ of this class is inspired by the implementation of Babel but incorporates
+ some ideas from the `ordereddict`_ and Django's ordered dict.
+
+ The constructor and `update()` both accept iterables of tuples as well as
+ mappings:
+
+ >>> d = odict([('a', 'b'), ('c', 'd')])
+ >>> d.update({'foo': 'bar'})
+ >>> d
+ odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
+
+ Keep in mind that when updating from dict-literals the order is not
+ preserved as these dicts are unsorted!
+
+ You can copy an odict like a dict by using the constructor, `copy.copy`
+ or the `copy` method and make deep copies with `copy.deepcopy`:
+
+ >>> from copy import copy, deepcopy
+ >>> copy(d)
+ odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
+ >>> d.copy()
+ odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
+ >>> odict(d)
+ odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
+ >>> d['spam'] = []
+ >>> d2 = deepcopy(d)
+ >>> d2['spam'].append('eggs')
+ >>> d
+ odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])])
+ >>> d2
+ odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', ['eggs'])])
+
+ All iteration methods as well as `keys`, `values` and `items` return
+ the values ordered by the the time the key-value pair is inserted:
+
+ >>> d.keys()
+ ['a', 'c', 'foo', 'spam']
+ >>> d.values()
+ ['b', 'd', 'bar', []]
+ >>> d.items()
+ [('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])]
+ >>> list(d.iterkeys())
+ ['a', 'c', 'foo', 'spam']
+ >>> list(d.itervalues())
+ ['b', 'd', 'bar', []]
+ >>> list(d.iteritems())
+ [('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])]
+
+ Index based lookup is supported too by `byindex` which returns the
+ key/value pair for an index:
+
+ >>> d.byindex(2)
+ ('foo', 'bar')
+
+ You can reverse the odict as well:
+
+ >>> d.reverse()
+ >>> d
+ odict.odict([('spam', []), ('foo', 'bar'), ('c', 'd'), ('a', 'b')])
+
+ And sort it like a list:
+
+ >>> d.sort(key=lambda x: x[0].lower())
+ >>> d
+ odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])])
+
+ .. _proposal: http://thread.gmane.org/gmane.comp.python.devel/95316
+ .. _ordereddict: http://www.xs4all.nl/~anthon/Python/ordereddict/
+ """
+
+ def __init__(self, *args, **kwargs):
+ dict.__init__(self)
+ self._keys = []
+ self.update(*args, **kwargs)
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ self._keys.remove(key)
+
+ def __setitem__(self, key, item):
+ if key not in self:
+ self._keys.append(key)
+ dict.__setitem__(self, key, item)
+
+ def __deepcopy__(self, memo=None):
+ if memo is None:
+ memo = {}
+ d = memo.get(id(self), missing)
+ if d is not missing:
+ return d
+ memo[id(self)] = d = self.__class__()
+ dict.__init__(d, deepcopy(self.items(), memo))
+ d._keys = self._keys[:]
+ return d
+
+ def __getstate__(self):
+ return {'items': dict(self), 'keys': self._keys}
+
+ def __setstate__(self, d):
+ self._keys = d['keys']
+ dict.update(d['items'])
+
+ def __reversed__(self):
+ return reversed(self._keys)
+
+ def __eq__(self, other):
+ if isinstance(other, odict):
+ if not dict.__eq__(self, other):
+ return False
+ return self.items() == other.items()
+ return dict.__eq__(self, other)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __cmp__(self, other):
+ if isinstance(other, odict):
+ return cmp(self.items(), other.items())
+ elif isinstance(other, dict):
+ return dict.__cmp__(self, other)
+ return NotImplemented
+
+ @classmethod
+ def fromkeys(cls, iterable, default=None):
+ return cls((key, default) for key in iterable)
+
+ def clear(self):
+ del self._keys[:]
+ dict.clear(self)
+
+ def copy(self):
+ return self.__class__(self)
+
+ def items(self):
+ return zip(self._keys, self.values())
+
+ def iteritems(self):
+ return izip(self._keys, self.itervalues())
+
+ def keys(self):
+ return self._keys[:]
+
+ def iterkeys(self):
+ return iter(self._keys)
+
+ def pop(self, key, default=missing):
+ if default is missing:
+ return dict.pop(self, key)
+ elif key not in self:
+ return default
+ self._keys.remove(key)
+ return dict.pop(self, key, default)
+
+ def popitem(self, key):
+ self._keys.remove(key)
+ return dict.popitem(key)
+
+ def setdefault(self, key, default=None):
+ if key not in self:
+ self._keys.append(key)
+ dict.setdefault(self, key, default)
+
+ def update(self, *args, **kwargs):
+ sources = []
+ if len(args) == 1:
+ if hasattr(args[0], 'iteritems'):
+ sources.append(args[0].iteritems())
+ else:
+ sources.append(iter(args[0]))
+ elif args:
+ raise TypeError('expected at most one positional argument')
+ if kwargs:
+ sources.append(kwargs.iteritems())
+ for iterable in sources:
+ for key, val in iterable:
+ self[key] = val
+
+ def values(self):
+ return map(self.get, self._keys)
+
+ def itervalues(self):
+ return imap(self.get, self._keys)
+
+ def index(self, item):
+ return self._keys.index(item)
+
+ def byindex(self, item):
+ key = self._keys[item]
+ return (key, dict.__getitem__(self, key))
+
+ def reverse(self):
+ self._keys.reverse()
+
+ def sort(self, *args, **kwargs):
+ self._keys.sort(*args, **kwargs)
+
+ def __repr__(self):
+ return 'odict.odict(%r)' % self.items()
+
+ __copy__ = copy
+ __iter__ = iterkeys
+
+
+if __name__ == '__main__':
+ import doctest
+ doctest.testmod()
# -*- coding: utf-8 -*-
'''
-libhooke.py
+config.py
-General library of internal objects and utilities for Hooke.
+Configuration defaults, read/write, and template file creation for Hooke.
-Copyright (C) 2006 Massimo Sandal (University of Bologna, Italy).
-With algorithms contributed by Francesco Musiani (University of Bologna, Italy)
-
-This program is released under the GNU General Public License version 2.
+COPYRIGHT
'''
-import scipy
-import numpy
-import xml.dom.minidom
-import os
+import ConfigParser as configparser
import os.path
-import string
-
-#import ConfigParser
-
-def config_file_path(filename, config_dir=None):
- if config_dir == None:
- config_dir = os.path.abspath(
- os.path.join(os.path.dirname(os.path.dirname(__file__)), 'conf'))
- return os.path.join(config_dir, filename)
-
-class HookeConfig(object):
- '''
- Handling of Hooke configuration file
-
- Mostly based on the simple-yet-useful examples of the Python Library Reference
- about xml.dom.minidom
-
- FIXME: starting to look a mess, should require refactoring
- '''
-
- def __init__(self, config_dir=None):
- self.config={}
- self.config['install']={}
- self.config['plugins']=[]
- self.config['drivers']=[]
- self.config['plotmanips']=[]
- self.config_dir = config_dir
-
- def load_config(self, filename):
- myconfig=file(config_file_path(filename, config_dir=self.config_dir))
-
- #the following 3 lines are needed to strip newlines. otherwise, since newlines
- #are XML elements too, the parser would read them (and re-save them, multiplying
- #newlines...)
- #yes, I'm an XML n00b
- the_file=myconfig.read()
- the_file_lines=the_file.split('\n')
- the_file=''.join(the_file_lines)
-
- self.config_tree=xml.dom.minidom.parseString(the_file)
-
- def getText(nodelist):
- #take the text from a nodelist
- #from Python Library Reference 13.7.2
- rc = ''
- for node in nodelist:
- if node.nodeType == node.TEXT_NODE:
- rc += node.data
- return rc
-
- def handleConfig(config):
- install_elements=config.getElementsByTagName("install")
- display_elements=config.getElementsByTagName("display")
- plugins_elements=config.getElementsByTagName("plugins")
- drivers_elements=config.getElementsByTagName("drivers")
- defaultlist_elements=config.getElementsByTagName("defaultlist")
- plotmanip_elements=config.getElementsByTagName("plotmanips")
- handleInstall(install_elements)
- handleDisplay(display_elements)
- handlePlugins(plugins_elements)
- handleDrivers(drivers_elements)
- handleDefaultlist(defaultlist_elements)
- handlePlotmanip(plotmanip_elements)
-
- def handleInstall(install_elements):
- for install in install_elements:
- for node in install.childNodes:
- if node.nodeType == node.TEXT_NODE:
- continue
- path = os.path.abspath(getText(node.childNodes).strip())
- self.config['install'][str(node.tagName)] = path
-
- def handleDisplay(display_elements):
- for element in display_elements:
- for attribute in element.attributes.keys():
- self.config[attribute]=element.getAttribute(attribute)
-
- def handlePlugins(plugins):
- for plugin in plugins[0].childNodes:
- try:
- self.config['plugins'].append(str(plugin.tagName))
- except: #if we allow fancy formatting of xml, there is a text node, so tagName fails for it...
- pass
- #FIXME: code duplication
- def handleDrivers(drivers):
- for driver in drivers[0].childNodes:
- try:
- self.config['drivers'].append(str(driver.tagName))
- except: #if we allow fancy formatting of xml, there is a text node, so tagName fails for it...
- pass
-
- def handlePlotmanip(plotmanips):
- for plotmanip in plotmanips[0].childNodes:
- try:
- self.config['plotmanips'].append(str(plotmanip.tagName))
- except: #if we allow fancy formatting of xml, there is a text node, so tagName fails for it...
- pass
-
- def handleDefaultlist(defaultlist):
- '''
- default playlist
- '''
- dflist=getText(defaultlist[0].childNodes)
- self.config['defaultlist']=dflist.strip()
-
- handleConfig(self.config_tree)
- #making items in the dictionary more machine-readable
- for item in self.config.keys():
- try:
- self.config[item]=float(self.config[item])
- except TypeError: #we are dealing with a list, probably. keep it this way.
- try:
- self.config[item]=eval(self.config[item])
- except: #not a list, not a tuple, probably a string?
- pass
- except ValueError: #if we can't get it to a number, it must be None or a string
- if string.lower(self.config[item])=='none':
- self.config[item]=None
- else:
- pass
-
- return self.config
-
-
- def save_config(self, config_filename):
- print 'Not Implemented.'
- pass
-
-
-def debug():
- '''
- debug stuff from latest rewrite of hooke_playlist.py
- should be removed sooner or later (or substituted with new debug code!)
- '''
- confo=HookeConfig()
- print confo.load_config('hooke.conf')
+import textwrap
+
+from .compat.odict import odict as OrderedDict
+
+DEFAULT_PATHS = [
+ '/usr/share/hooke/hooke.cfg',
+ '/etc/hooke/hooke.cfg',
+ '~/.hooke.cfg',
+ ]
+"""We start with the system files, and work our way to the more
+specific user files, so the user can override the sysadmin who
+in turn overrides the developer defaults.
+"""
+
+class Setting (object):
+ """An entry (section or option) in HookeConfigParser.
+ """
+ def __init__(self, section, option=None, value=None, help=None, wrap=True):
+ self.section = section
+ self.option = option
+ self.value = value
+ self.help = help
+ self.wrap = True
+
+ def is_section(self):
+ return self.option == None
+
+ def is_option(self):
+ return not self.is_section()
+
+ def write(self, fp, value=None, comments=True, wrapper=None):
+ if comments == True and self.help != None:
+ text = self.help
+ if self.wrap == True:
+ if wrapper == None:
+ wrapper = textwrap.TextWrapper(
+ initial_indent="# ",
+ subsequent_indent="# ")
+ text = wrapper.fill(text)
+ fp.write(text.rstrip()+'\n')
+ if self.is_section():
+ fp.write("[%s]\n" % self.section)
+ else:
+ fp.write("%s = %s\n" % (self.option,
+ str(value).replace('\n', '\n\t')))
+
+DEFAULT_SETTINGS = [
+ Setting('display', help='Control display appearance: colour, ???, etc.'),
+ Setting('display', 'colour_ext', 'None', help=None),
+ Setting('display', 'colour_ret', 'None', help=None),
+ Setting('display', 'ext', '1', help=None),
+ Setting('display', 'ret', '1', help=None),
+
+ Setting('display', 'correct', '1', help=None),
+ Setting('display', 'colout_correct', 'None', help=None),
+ Setting('display', 'contact_point', '0', help=None),
+ Setting('display', 'medfilt', '0', help=None),
+
+ Setting('display', 'xaxes', '0', help=None),
+ Setting('display', 'yaxes', '0', help=None),
+ Setting('display', 'flatten', '1', help=None),
+
+ Setting('conditions', 'temperature', '301', help=None),
+
+ Setting('fitting', 'auto_fit_points', '50', help=None),
+ Setting('fitting', 'auto_slope_span', '20', help=None),
+ Setting('fitting', 'auto_delta_force', '1-', help=None),
+ Setting('fitting', 'auto_fit_nm', '5', help=None),
+ Setting('fitting', 'auto_min_p', '0.005', help=None),
+ Setting('fitting', 'auto_max_p', '10', help=None),
+
+ Setting('?', 'baseline_clicks', '0', help=None),
+ Setting('fitting', 'auto_left_baseline', '20', help=None),
+ Setting('fitting', 'auto_right_baseline', '20', help=None),
+ Setting('fitting', 'force_multiplier', '1', help=None),
+
+ Setting('display', 'fc_showphase', '0', help=None),
+ Setting('display', 'fc_showimposed', '0', help=None),
+ Setting('display', 'fc_interesting', '0', help=None),
+ Setting('?', 'tccd_threshold', '0', help=None),
+ Setting('?', 'tccd_coincident', '0', help=None),
+ Setting('display', '', '', help=None),
+ Setting('display', '', '', help=None),
+
+ Setting('filesystem', 'filterindex', '0', help=None),
+ Setting('filesystem', 'filters',
+ "Playlist files (*.hkp)|*.hkp|Text files (*.txt)|*.txt|All files (*.*)|*.*')",
+ help=None),
+ Setting('filesystem', 'workdir', 'test',
+ help='\n'.join(['# Substitute your work directory',
+ '#workdir = D:\hooke']),
+ wrap=False),
+ Setting('filesystem', 'playlist', 'test.hkp', help=None),
+ ]
+
+def get_setting(settings, match):
+ """Return the first Setting object matching both match.section and
+ match.option.
+ """
+ for s in settings:
+ if s.section == match.section and s.option == match.option:
+ return s
+ return None
+
+class HookeConfigParser (configparser.SafeConfigParser):
+ """A wrapper around configparser.SafeConfigParser.
+
+ You will probably only need .read and .write.
+
+ Examples
+ --------
+
+ >>> import sys
+ >>> c = HookeConfigParser(paths=DEFAULT_PATHS,
+ ... default_settings=DEFAULT_SETTINGS)
+ >>> c.write(sys.stdout) # doctest: +ELLIPSIS
+ # Control display appearance: colour, ???, etc.
+ [display]
+ colour_ext = None
+ colour_ret = None
+ ...
+ """
+ def __init__(self, paths=None, default_settings=None, defaults=None,
+ dict_type=OrderedDict, indent='# ', **kwargs):
+ # Can't use super() because SafeConfigParser is a classic class
+ #super(HookeConfigParser, self).__init__(defaults, dict_type)
+ configparser.SafeConfigParser.__init__(self, defaults, dict_type)
+ if paths == None:
+ paths = []
+ self._config_paths = paths
+ if default_settings == None:
+ default_settings = []
+ self._default_settings = default_settings
+ for key in ['initial_indent', 'subsequent_indent']:
+ if key not in kwargs:
+ kwargs[key] = indent
+ self._comment_textwrap = textwrap.TextWrapper(**kwargs)
+ self._setup_default_settings()
+
+ def _setup_default_settings(self):
+ for setting in self._default_settings: #reversed(self._default_settings):
+ # reversed cause: http://docs.python.org/library/configparser.html
+ # "When adding sections or items, add them in the reverse order of
+ # how you want them to be displayed in the actual file."
+ if setting.section not in self.sections():
+ self.add_section(setting.section)
+ if setting.option != None:
+ self.set(setting.section, setting.option, setting.value)
+
+ def read(self, filenames=None):
+ """Read and parse a filename or a list of filenames.
+
+ If filenames is None, it defaults to ._config_paths. If a
+ filename is not in ._config_paths, it gets appended to the
+ list. We also run os.path.expanduser() on the input filenames
+ internally so you don't have to worry about it.
+
+ Files that cannot be opened are silently ignored; this is
+ designed so that you can specify a list of potential
+ configuration file locations (e.g. current directory, user's
+ home directory, systemwide directory), and all existing
+ configuration files in the list will be read. A single
+ filename may also be given.
+
+ Return list of successfully read files.
+ """
+ if filenames == None:
+ filenames = [os.path.expanduser(p) for p in self._config_paths]
+ else:
+ if isinstance(filenames, basestring):
+ filenames = [filenames]
+ paths = [os.path.abspath(os.path.expanduser(p))
+ for p in self._config_paths]
+ for filename in filenames:
+ if os.path.abspath(os.path.expanduser(filename)) not in paths:
+ self._config_paths.append(filename)
+ # Can't use super() because SafeConfigParser is a classic class
+ #return super(HookeConfigParser, self).read(filenames)
+ return configparser.SafeConfigParser.read(self, filenames)
+
+ def _write_setting(self, fp, section=None, option=None, value=None,
+ **kwargs):
+ """Print, if known, a nicely wrapped comment form of a
+ setting's .help.
+ """
+ match = get_setting(self._default_settings, Setting(section, option))
+ if match == None:
+ match = Setting(section, option, value, **kwargs)
+ match.write(fp, value=value, wrapper=self._comment_textwrap)
+
+ def write(self, fp=None, comments=True):
+ """Write an .ini-format representation of the configuration state.
+
+ This expands on RawConfigParser.write() by optionally adding
+ comments when they are known (i.e. for ._default_settings).
+ However, comments are not read in during a read, so .read(x)
+ .write(x) may `not preserve comments`_.
+
+ .. _not preserve comments: http://bugs.python.org/issue1410680
+
+ Examples
+ --------
+
+ >>> import sys, StringIO
+ >>> c = HookeConfigParser()
+ >>> instring = '''
+ ... # Some comment
+ ... [section]
+ ... option = value
+ ...
+ ... '''
+ >>> c._read(StringIO.StringIO(instring), 'example.cfg')
+ >>> c.write(sys.stdout)
+ [section]
+ option = value
+ <BLANKLINE>
+ """
+ local_fp = fp == None
+ if local_fp:
+ fp = open(self._config_paths[-1], 'w')
+ if self._defaults:
+ print self._defaults
+ self._write_setting(fp, configparser.DEFAULTSECT,
+ help="Miscellaneous options")
+ for (key, value) in self._defaults.items():
+ self._write_setting(fp, configparser.DEFAULTSECT, key, value)
+ fp.write("\n")
+ for section in self._sections:
+ self._write_setting(fp, section)
+ for (key, value) in self._sections[section].items():
+ if key != "__name__":
+ self._write_setting(fp, section, key, value)
+ fp.write("\n")
+ if local_fp:
+ fp.close()
#!/usr/bin/env python
'''
-HOOKE - A force spectroscopy review & analysis tool
+Hooke - A force spectroscopy review & analysis tool.
-Copyright (C) 2008-2010 Massimo Sandal (University of Bologna, Italy).
- Rolf Schmidt (Concordia University, Canada).
-
-This program is released under the GNU General Public License version 2.
+COPYRIGHT
'''
-from libhooke import WX_GOOD
+import Queue as queue
+import multiprocessing
+
+from . import config as config_mod
+from .plugin import PLUGINS
+from .driver import DRIVERS
+
-import wxversion
-wxversion.select(WX_GOOD)
import copy
import cStringIO
import os
import glob
import time
-import imp
-import wx
-import wx.html
-import wx.aui
-import wxmpl
-import wx.lib.agw.aui as aui
-import wx.propgrid as wxpg
-
-import libhooke as lh
-from config import config
-import drivers
-import plugins
-import hookecommands
-import hookeplaylist
-import hookepropertyeditor
-import hookeresults
-import playlist
-
-global __version__
-
-__version__ = lh.HOOKE_VERSION[0]
-__release_name__ = lh.HOOKE_VERSION[1]
-
-#TODO: order menu items, get rid of all unused IDs
-ID_ExportText = wx.NewId()
-ID_ExportImage = wx.NewId()
-ID_Config = wx.NewId()
-ID_About = wx.NewId()
-ID_Next = wx.NewId()
-ID_Previous = wx.NewId()
-
-ID_ViewAssistant = wx.NewId()
-ID_ViewCommands = wx.NewId()
-ID_ViewFolders = wx.NewId()
-ID_ViewOutput = wx.NewId()
-ID_ViewPlaylists = wx.NewId()
-ID_ViewProperties = wx.NewId()
-ID_ViewResults = wx.NewId()
-
-ID_CommandsList = wx.NewId()
-ID_CommandsListBox = wx.NewId()
-
-ID_TextContent = wx.NewId()
-ID_TreeContent = wx.NewId()
-ID_HTMLContent = wx.NewId()
-ID_SizeReportContent = wx.NewId()
-ID_DeletePerspective = wx.NewId()
-ID_SavePerspective = wx.NewId()
-
-ID_FirstPerspective = ID_SavePerspective + 1000
-#I hope we'll never have more than 1000 perspectives
-ID_FirstPlot = ID_SavePerspective + 2000
-
-class Hooke(wx.App):
-
- def OnInit(self):
- self.SetAppName('Hooke')
- self.SetVendorName('')
-
- #set the Hooke directory
- lh.hookeDir = os.path.abspath(os.path.dirname(__file__))
-
- windowPosition = (config['main']['left'], config['main']['top'])
- windowSize = (config['main']['width'], config['main']['height'])
-
- #setup the splashscreen
- if config['splashscreen']['show']:
- filename = lh.get_file_path('hooke.jpg', ['resources'])
- if os.path.isfile(filename):
- bitmap = wx.Image(filename).ConvertToBitmap()
- splashStyle = wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT
- splashDuration = config['splashscreen']['duration']
- wx.SplashScreen(bitmap, splashStyle, splashDuration, None, -1)
- wx.Yield()
- '''
- we need for the splash screen to disappear
- for whatever reason splashDuration and sleep do not correspond to each other
- at least not on Windows
- maybe it's because duration is in milliseconds and sleep in seconds
- thus we need to increase the sleep time a bit
- a factor of 1.2 seems to work quite well
- '''
- sleepFactor = 1.2
- time.sleep(sleepFactor * splashDuration / 1000)
-
- plugin_objects = []
- for plugin in config['plugins']:
- if config['plugins'][plugin]:
- filename = ''.join([plugin, '.py'])
- path = lh.get_file_path(filename, ['plugins'])
- if os.path.isfile(path):
- #get the corresponding filename and path
- plugin_name = ''.join(['plugins.', plugin])
- module = __import__(plugin_name)
- #get the file that contains the plugin
- class_file = getattr(plugins, plugin)
- #get the class that contains the commands
- class_object = getattr(class_file, plugin + 'Commands')
- plugin_objects.append(class_object)
+#import libhooke as lh
+#import drivers
+#import plugins
+#import hookecommands
+#import hookeplaylist
+#import hookepropertyeditor
+#import hookeresults
+#import playlist
+
+class Hooke(object):
+ def __init__(self, config=None, debug=0):
+ self.debug = debug
+ config_mod.DEFAULTS.extend(self.plugin_default_settings())
+ config_mod.DEFAULTS.extend(self.driver_default_settings())
+ if config == None:
+ config = config_mod.HookeConfigParser(
+ paths=config_mod.DEFAULT_PATHS,
+ default_settings=config_mod.DEFAULT_SETTINGS)
+ config.read_configs()
+ self.config = config
+ self.load_plugins()
+ self.load_drivers()
+
+ def plugin_default_settings(self):
+ pass
- def make_command_class(*bases):
- #create metaclass with plugins and plotmanipulators
- return type(HookeFrame)("HookeFramePlugged", bases + (HookeFrame,), {})
- frame = make_command_class(*plugin_objects)(parent=None, id=wx.ID_ANY, title='Hooke', pos=windowPosition, size=windowSize)
- frame.Show(True)
- self.SetTopWindow(frame)
+ def driver_default_settings(self):
+ pass
- return True
+ def load_plugins(self):
+ pass
- def OnExit(self):
- #TODO: write values to ini file if necessary
- return True
+ def load_drivers(self)
+ pass
+ def close(self):
+ if self.config.changed:
+ self.config.write() # Does not preserve original comments
class HookeFrame(wx.Frame):
self.AppendToOutput('Not able to plot.')
-ID_PaneBorderSize = wx.ID_HIGHEST + 1
-ID_SashSize = ID_PaneBorderSize + 1
-ID_CaptionSize = ID_PaneBorderSize + 2
-ID_BackgroundColor = ID_PaneBorderSize + 3
-ID_SashColor = ID_PaneBorderSize + 4
-ID_InactiveCaptionColor = ID_PaneBorderSize + 5
-ID_InactiveCaptionGradientColor = ID_PaneBorderSize + 6
-ID_InactiveCaptionTextColor = ID_PaneBorderSize + 7
-ID_ActiveCaptionColor = ID_PaneBorderSize + 8
-ID_ActiveCaptionGradientColor = ID_PaneBorderSize + 9
-ID_ActiveCaptionTextColor = ID_PaneBorderSize + 10
-ID_BorderColor = ID_PaneBorderSize + 11
-ID_GripperColor = ID_PaneBorderSize + 12
-
-
-#----------------------------------------------------------------------
-
if __name__ == '__main__':
-
- ## now, silence a deprecation warning for py2.3
- #import warnings
- #warnings.filterwarnings("ignore", "integer", DeprecationWarning, "wxPython.gdi")
-
- redirect=True
- if __debug__:
- redirect=False
- app = Hooke(redirect=redirect)
-
+ app = Hooke(debug=__debug__)
app.MainLoop()