guidata-1.7.4/0000755000000000000000000000000012632514315011641 5ustar rootrootguidata-1.7.4/MANIFEST.in0000666000000000000000000000033512617402724013407 0ustar rootrootrecursive-include guidata *.inc *.png *.svg *.pot *.po *.mo recursive-include doc *.py *.rst *.png *.ico include MANIFEST.in include Licence_CeCILL_V2-en.txt include README include changelog prune guidata/tests/distguidata-1.7.4/doc/0000755000000000000000000000000012632514315012406 5ustar rootrootguidata-1.7.4/doc/development.rst0000666000000000000000000001310112604157370015465 0ustar rootrootHow to contribute ================= Submitting changes ------------------ Due to confidentiality issues, we are not able *for now* to publish any source- controlled repository (even if we do have a `Mercurial` repository for the project). However, this does not prevent motivated users from contributing to the project by sending patches applied to the last published version of the library. To compensate the absence of source repository, we try to update the library as often as we can in order to keep the public source archive version as close as possible to the internal development version. Coding guidelines ----------------- In general, we try to follow the standard Python coding guidelines, which cover all the important coding aspects (docstrings, comments, naming conventions, import statements, ...) as described here: * `Style Guide for Python Code `_ The easiest way to check that your code is following those guidelines is to run `pylint` (a note greater than 8/10 seems to be a reasonnable goal). PyQt v4.4 compatibility issues ------------------------------ The project has to be compatible with PyQt >= v4.4 which means that the following recommendations should be followed: * avoid using `super`: when writing classes deriving from a QObject child class (i.e. almost any single class imported from QtGui or QtCore), the `super` builtin-function should not be used outside the constructor method (call the parent class method directly instead) * before using any function or method from PyQt4, please check that the feature you are about to use was already implemented in PyQt4 v4.4 (more precisely in the Qt version used in PyQt4 v4.4) -- if not, a workaround should be implemented to avoid breaking compatibility * do not use the PyQt-specific QFileDialog static methods (not present in Qt) which were introduced in PyQt v4.6: `getOpenFileNameAndFilter`, `getOpenFileNamesAndFilter` and `getSaveFileNameAndFilter` (`guidata` provides wrappers around `QFileDialog` static methods handling the selected filter which were taken from the `spyderlib` library (from module `spyderlib.qt.compat`): they are available in `guidata.qt.compat`) PyQt / PySide compatibility --------------------------- The project should be mostly compatible with both PyQt and PySide (although PySide is not as popular as it used to be, so testing tend to be limited). PyQt5 compatibility ------------------- In its current implementation, the code base has to be compatible with PyQt API #2 (PySide-compatible API, PyQt >= v4.6) and with PyQt5, which means that the following recommendations should be followed: * `QVariant` objects must not be used (API #2 compatibility) * Use exclusively new-style signals and slots * Read carefully PyQt5 documentation regarding class inheritance behavior: it is quite different than the old PyQt4 implementation. Producing code compatible with both PyQt4 and PyQt5 can be tricky: testing is essential. Python 3 compatibility ====================== Regarding Python 3 compatibility, we chose to handle it by maintaining a single source branch being compatible with both Python 2.6-2.7 and Python 3. Here is what we have done. Fixing trivial things with 2to3 ------------------------------- The first step is to run the `2to3` script (see Python documentation) to convert print statements to print function calls -- note that your source directory (named `directory_name`) has to be version controlled (no backup is done thanks to the `-n` option flag). `python 2to3.py -w -n -f print directory_name` Open each modified source file and add the following line at the beginning: from __future__ import print_function Then run again `2to3` with all other Python 2/3 compatible fixers: `python 2to3.py -w -n -f apply -f dict -f except -f exitfunc -f filter -f has_key -f map -f ne -f raise -f ws_comma -f xrange -f xreadlines -f zip directory_name` After these two steps, your code should be compatible with Python 2.6, 2.7 and 3.x, but only with respect to the simplest changes that occured between Python 2 and Python 3. However, this a step forward to Python 3 compatibility without breaking Python 2.6+ compatibility. Fixing unicode issues --------------------- In Python 3, `unicode` and `str` strings have been replaced by `str` and `bytes` strings: * `str` is the text string type, supporting unicode characters natively * `bytes` is the binary string type. As a consequence, Python 2 code involving strings may cause compatibility issues with Python 3. For example: * file I/O may return `bytes` instead of `str` in Python 3 (depending on the open mode): this can be solved by calling the `decode` method on the `bytes` object (this will work on both Python 2 `str` and Python 3 `bytes` objects) * in Python 3.0-3.2, the `u'unicode text'` or `u"unicode text"` syntax is not allowed and will raise a SyntaxError: this can be solved by inserting the `from __future__ import unicode_literals` at the beginning of the script and by removing all the `u` string prefixes * in Python 3 `isinstance(text, basestring)` can be replaced by `is_text_string(text)` (function of the `guidata.py3compat` module) * in Python 3 `isinstance(text, unicode)` can be replaced by `is_unicode(text)` (function of the `guidata.py3compat` module) * in Python 3 `unicode(text)` can be replaced by `to_text_string(text)` (function of the `guidata.py3compat` module) guidata-1.7.4/doc/conf.py0000666000000000000000000001574312605166264013731 0ustar rootroot# -*- coding: utf-8 -*- # # Spyder documentation build configuration file, created by # sphinx-quickstart on Fri Jul 10 16:32:25 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. from __future__ import print_function, unicode_literals import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] try: import sphinx.ext.viewcode extensions.append('sphinx.ext.viewcode') except ImportError: print("WARNING: the Sphinx viewcode extension was not found", file=sys.stderr) # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = 'guidata' import time this_year = time.strftime("%Y", time.localtime()) copyright = "2009-%s, CEA - Commissariat à l'Energie Atomique et aux Energies Alternatives" % this_year # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. import guidata version = ".".join(guidata.__version__.split('.')[:2]) # The full version, including alpha/beta/rc tags. release = guidata.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['guidata.'] autodoc_member_order = 'bysource' # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'sphinxdoc' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. ## html_theme_options = {'sidebarbgcolor': '#227A2B', ## 'sidebarlinkcolor': '#98ff99'} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = '%s %s Manual' % (project, version) # A shorter title for the navigation bar. Default is the same as html_title. html_short_title = '%s Manual' % project # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'images/guidata.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = '_static/favicon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'guidata' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'guidata.tex', 'guidata Manual', 'Pierre Raybaut', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True guidata-1.7.4/doc/reference/0000755000000000000000000000000012632514315014344 5ustar rootrootguidata-1.7.4/doc/reference/dataset.rst0000666000000000000000000000005712605247676016546 0ustar rootroot.. automodule:: guidata.dataset :members: guidata-1.7.4/doc/reference/userconfig.rst0000666000000000000000000000006212605247676017261 0ustar rootroot.. automodule:: guidata.userconfig :members: guidata-1.7.4/doc/reference/index.rst0000666000000000000000000000024712605247676016231 0ustar rootrootReference ========= guidata API: .. toctree:: :maxdepth: 2 dataset qthelpers disthelpers configtools userconfig utils guidata-1.7.4/doc/reference/configtools.rst0000666000000000000000000000006312605247676017444 0ustar rootroot.. automodule:: guidata.configtools :members: guidata-1.7.4/doc/reference/utils.rst0000666000000000000000000000005512605247676016257 0ustar rootroot.. automodule:: guidata.utils :members: guidata-1.7.4/doc/reference/qthelpers.rst0000666000000000000000000000006112605247676017123 0ustar rootroot.. automodule:: guidata.qthelpers :members: guidata-1.7.4/doc/reference/disthelpers.rst0000666000000000000000000000006312605247676017444 0ustar rootroot.. automodule:: guidata.disthelpers :members: guidata-1.7.4/doc/examples.rst0000666000000000000000000000520012610723002014746 0ustar rootrootExamples ======== Basic example ------------- Source code : :: import guidata _app = guidata.qapplication() # not required if a QApplication has already been created import guidata.dataset.datatypes as dt import guidata.dataset.dataitems as di class Processing(dt.DataSet): """Example""" a = di.FloatItem("Parameter #1", default=2.3) b = di.IntItem("Parameter #2", min=0, max=10, default=5) type = di.ChoiceItem("Processing algorithm", ("type 1", "type 2", "type 3")) param = Processing() param.edit() Output : .. image:: images/basic_example.png Assigning values to data items or using these values is very easy : :: param.a = 5.34 param.type = "type 3" print "a*b =", param.a*param.b Other examples -------------- A lot of examples are available in the `guidata` test module :: from guidata import tests tests.run() The two lines above execute the `guidata test launcher` : .. image:: images/screenshots/__init__.png All `guidata` items demo ~~~~~~~~~~~~~~~~~~~~~~~~ .. literalinclude:: ../guidata/tests/all_items.py :start-after: SHOW :end-before: Workaround for Sphinx v0.6 bug: empty 'end-before' directive .. image:: images/screenshots/all_items.png All (GUI-related) `guidata` features demo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. literalinclude:: ../guidata/tests/all_features.py :start-after: SHOW :end-before: Workaround for Sphinx v0.6 bug: empty 'end-before' directive .. image:: images/screenshots/all_features.png Embedding guidata objects in GUI layouts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. literalinclude:: ../guidata/tests/editgroupbox.py :start-after: SHOW :end-before: Workaround for Sphinx v0.6 bug: empty 'end-before' directive .. image:: images/screenshots/editgroupbox.png Data item groups and group selection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. literalinclude:: ../guidata/tests/bool_selector.py :start-after: SHOW :end-before: Workaround for Sphinx v0.6 bug: empty 'end-before' directive .. image:: images/screenshots/bool_selector.png Activable data sets ~~~~~~~~~~~~~~~~~~~ .. literalinclude:: ../guidata/tests/activable_dataset.py :start-after: SHOW :end-before: Workaround for Sphinx v0.6 bug: empty 'end-before' directive .. image:: images/screenshots/activable_dataset.png Data set groups ~~~~~~~~~~~~~~~ .. literalinclude:: ../guidata/tests/datasetgroup.py :start-after: SHOW :end-before: Workaround for Sphinx v0.6 bug: empty 'end-before' directive .. image:: images/screenshots/datasetgroup.png guidata-1.7.4/doc/index.rst0000666000000000000000000000141612605165376014266 0ustar rootroot.. automodule:: guidata .. only:: html and not htmlhelp .. note:: Windows users may download the :download:`CHM Manual <../guidata.chm.zip>`. After downloading this file, you may see blank pages in the documentation. That's because Windows is blocking CHM files for security reasons. Fixing this problem is easy: * Right-click the CHM file, select properties, then click “Unblock”. * Or compress the CHM file into a zip archive and decompress it in another directory. * Do not open the CHM file on a network drive. Contents: .. toctree:: :maxdepth: 2 :glob: overview installation development examples reference/index Indices and tables: * :ref:`genindex` * :ref:`search` guidata-1.7.4/doc/basic_example.py0000666000000000000000000000107712604157370015570 0ustar rootroot# -*- coding: utf-8 -*- # Note: the two following lines are not required # if a QApplication has already been created import guidata _app = guidata.qapplication() import guidata.dataset.datatypes as dt import guidata.dataset.dataitems as di class Processing(dt.DataSet): """Example""" a = di.FloatItem("Parameter #1", default=2.3) b = di.IntItem("Parameter #2", min=0, max=10, default=5) type = di.ChoiceItem("Processing algorithm", ("type 1", "type 2", "type 3")) param = Processing() param.edit() guidata-1.7.4/doc/_static/0000755000000000000000000000000012632514315014034 5ustar rootrootguidata-1.7.4/doc/_static/favicon.ico0000666000000000000000000001731612604157370016174 0ustar rootroot  6  hf( @  |||S///t&  ,'''u:" ...-444B???^WWWUUU###pTF;-999VVV@qqq됐UUUhH3  jjj=ggghG.aaajQQQT8rrrY9Y6c]HIP(e\Q?;;;>pbaZODQ"bb555z.̺jF}dEcbb`VK@9hhh5zKlAgCcbb^TI>@7v=q?kBtRG<3Gu=o@jBeD|cE|cE}dF[OP*¢|?t>n@iB~dE|cE|cE}eGa\Ğɾ|cE|cEgggg&r?lAgC}cE|cE%ũy@kAfDfIl }Ip@sMķGGGYYYĊޛ_(ũޮ^ŭзL?(0 gggl1  WWW+!!!P+++ 2224aaa\111p A-!... PPP*uuurഴvvv555oD%TTT{{{1WWWJ$iii=(((Z"5H%ĺTdFmbZp]]]|&b<|hEmbaUH<===SvClA]En\\\Y|Cq?rKottŽPw```Q¥norjiLuRRR8ˉ̸oRs8886[xfDuކL Usv񚚚cd湹@@@ QβYgμɻw Chz~~ykCAAA?AAAAAAAAAAAAAAAAAAA?AA(   IIII# GGGqqq^盛WWW{ ?YYY^nnn48Utn!bG^qbU@ffftx@uNQy̵͸z`gggMregCӵ̸ɸ쉉4ϖ͞04͓ϸ[{{{?A?AAAAAAAAAAAAAAAguidata-1.7.4/doc/installation.rst0000666000000000000000000000156312626552152015656 0ustar rootrootInstallation ============ Dependencies ------------ Requirements: * Python 2.x (x>=6) or 3.x (x>=2) * PyQt4 4.x (x>=3 ; recommended x>=4) or PyQt5 5.x (x>=5) * spyderlib >=v2.0.10 for the test launcher and array/dictionnary editors Optional Python modules: * h5py (HDF5 files I/O) * cx_Freze or py2exe (application deployment on Windows platforms) Other optional modules for developers: * gettext (text translation support) Installation ------------ From the source package: `python setup.py install` Help and support ---------------- External resources: * Bug reports and feature requests: `GitHub`_ * Help, support and discussions around the project: `GoogleGroup`_ .. _GitHub: https://github.com/PierreRaybaut/guidata .. _GoogleGroup: http://groups.google.fr/group/guidata_guiqwt guidata-1.7.4/doc/overview.rst0000666000000000000000000000412212604157370015014 0ustar rootrootOverview ======== When developping scientific software, from the simplest script to the most complex application, one systematically needs to manipulate data sets (e.g. parameters for a data processing feature). These data sets may consist of various data types: real numbers (e.g. physical quantities), integers (e.g. array indexes), strings (e.g. filenames), booleans (e.g. enable/disable an option), and so on. Most of the time, the programmer will need the following features: * allow the user to enter each parameter through a graphical user interface, using widgets which are adapted to data types (e.g. a single combo box or check boxes are suitable for presenting an option selection among multiple choices) * entered values have to be stored by the program with a convention which is again adapted to data types (e.g. when storing a combo box selection value, should we store the option string, the list index or an associated key?) * using the stored values easily (e.g. for data processing) by regrouping parameters in data structures * showing the stored values in a dialog box or within a graphical user interface layout, again with widgets adapted to data types This library aims to provide these features thanks to automatic graphical user interface generation for data set editing and display. Widgets inside GUIs are automatically generated depending on each data item type. The `guidata` library also provides the following features: * :py:mod:`guidata.qthelpers`: `PyQt4` helpers * :py:mod:`guidata.disthelpers`: `py2ex` helpers * :py:mod:`guidata.userconfig`: `.ini` configuration management helpers (based on Python standard module :py:mod:`ConfigParser`) * :py:mod:`guidata.configtools`: library/application data management * :py:mod:`guidata.gettext_helpers`: translation helpers (based on the GNU tool `gettext`) * :py:mod:`guidata.guitest`: automatic GUI-based test launcher * :py:mod:`guidata.utils`: miscelleneous utilities guidata-1.7.4/doc/images/0000755000000000000000000000000012632514315013653 5ustar rootrootguidata-1.7.4/doc/images/guidata.png0000666000000000000000000000655512604157370016021 0ustar rootrootPNG  IHDR2"gAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r IDATx^ypTU]˚jfjfʩbtaRIbX&%$A00laͰddPӝ@f$@ MQD6!Iߜ@wSu}{w9wy{-E-$-$ߐlfC7B/T4.  @Hऐ$m"{Fe]".T$G-Ĉ[ٌ2MW!Y쩷g0`>)v H?p˓ڮTp+hsƣ6z8WGkp7`B[}\mXlt-ɓņCپ![s${LMFw˺~*\~z$h $Á$׀FpN&I!Iō/ 22mkhC4HKYkF޾wX;W3!P Ϫ'bKt ‘3Qsi \s#p&Mcn\ݟvzh'x+즧B!oC* {p}s%Yr}jk8;`6 X\>J1n$΍.N`ݢkh05PԋwG`̓\U;&la\A<ex&6`qg3skma  Р`\ȹNDf CU`s(yhUx|_F,Z9z%侍o;7?~9Iz]CU뢃/`}VkLVŶ9 {Y@`dQsx*p~n~FDs,md`s,ǶXy;qث^uH"F2 z_{yZ]82V`kb`/X+gyb9V|ڿ$ö.WQLLZyeNHvՅP(kh6YO: Z%X ,98yg`-8#:ʱX(ա,k-XM%Ih?'3cN [T汆{+br+1xG|(H;rQ+nJZRVg,hU[v^kzz:<>zקN^ _SiG`]>$F^\$DydB2$/?yewޥ`YL8RYd`UL=*Sх_hͲn4pWDۑH>n7\8fErM`M)Xux+@o^Μ[DEL*vߣ4r419k4h;v.#=/ %n_bvA]i*2p7.35xCxn7BCaDK(0ɟͽW]K1 "'yU݌0"ړ:yP ("lj@4Xl|5=N^NXl¥JڎK~XwNU/l,b!2Ӝ_W0:bp5$>B9xs5w \+ VTV -c$Ouf}|'KhDjj*sIzh45ՔEWsO7޶?黜}ՁfG"3N'Lx8HQO/Oty_: "zJ!m\In6]EYNX5K1EFGG#.."Ć`fߜA jW|j2H( (BV*m"HMW +`7S>y`"ӫ2wi[b뺥Ź P7{/ooea;ne :LXcƌܧO &F὞, F`NWΠj:W tDNQT\ > HjVut&#`1ONHB8Iꔫ]OtKU)Ph~`NGD, R-@Ϟ=2V+RRRP ɐ"} 5%ՆgQz-RD}ߢ ,zCڒCrENAg_/NJJ=--uF/@ޑ# m/hD:7X%[wCkn^L(I ';PM~vTMya0`r`y:Lo$#D`m' cjOMy=%1!db}n EaVNkIl++>s'5e`֧`پn`y*:XރV$l~E)w젫Pl"ޒ`ފ-|Y5SSF)um_w<ODW> Q`DP_n!U@6r,qG^Us5RXb9@oOZz̋to_=N>P$ևa^ P t`$/U24 }o(z5-PTVUJ6%Ɩ;XUܹ*lUklQ2I:قy+!#w8JSٗxfN%_uKyNi|]>;+T:9J}'am!% HMRIRS| H]|͖bKl79T[8 4ZNP0+.fތ-+?@a -@a -@Y=̣7IENDB`guidata-1.7.4/doc/images/screenshots/0000755000000000000000000000000012632514315016213 5ustar rootrootguidata-1.7.4/doc/images/screenshots/editgroupbox.png0000666000000000000000000003733212604157370021453 0ustar rootrootPNG  IHDR >IDATx |eMr6E++hU (( Rv_'rŲY łP@ +ҖRzL2$;I>/<35jrgϞ5*={2dHӦM˫-ALLL\\\&Mv?N zѣrW QF-[fF A?^@Ѵk.??O?դ3ڵkr &!!!66v͌@L<_V@DJKK/^ZP[HRRR-xIi&''{ٳNs H]vҥ[o i\WWG TPh4`hlzF-3G^C,˕5ket޳ ĩS>++KS~ѣGr9 @%9s&==]VUUP__B\XPPбc-Wu k51T z Vc}vC)DkXK][h>0ѹsg@?~\ȩ=T`g?T4ѫsRcccRsܹj7+,,l߾} r_ϲR#uZB"C,RGNpA37ml'?rpPs ıcDo?'~okcjH7nX/P:ϟ})$$$о]v!,d󽷔])!TuECePmhYb41T&zi~xRRk!k֬#<"ZƁv-Hw)U11n+gϞv8t>xO_a%9k URvSرc[:ٍ{2Ym;޽ə#4>S 7}k \g[lܕjJJJv݋M49qwS!&MrpS!r巋q>]nn|25ozNrp.{W|W|! }Srr:5(`C;.s--j >G?wl=`'u~T'["̻'{ƶ)-ge ]h]cZP2p j UϼۉwZy:؁wnwީgQ]]MYy?…]6l޼9Nرc :RSSuxS}k+x^Ӻ-C"0x`qO,\2uulTdՒuQL5`g`::8>aU f>+gyћO['֥%&67f~3\1gU9|UnŽT=U~?Lws޹wlrzNZ;j'鴫>d?ټjɈb/>?Be 9emR;W%~:m)>ş&х3y:E[׉T3@P\v[nhرc\T|P֩nῇs#;}+pmV͍zea_ vWo;U{[⓷ӬP_{"m}us=נAzL]faw,ԫWߺp8*YD?Ҷ!7W\nW S=m84_"z$l`imZh%m{c摼5oijΚ?nsMݱJ7ӜvVqO~;=fѽ[p!S qCv1}G`'} Jybo9UkM4q-/8p kӷo?&Z]*xCԓV={ܹ7CbǎVZ=v[||<2{7Hä=OE[`]ptvp̮slL^wmjllsIέwv6.}p~`B#t:V5 tomڴh!> S!?ޝ\\phCmq j.X4I=?/ x W_}ՠb&dԡbbQݯ_ g{ s6L|i鈱;.g칎OIL[6L{ώVg;W*x4w݌4өU}`i\#9lժvՌef?tz:۶C:3rJbn{[`L~S^-{N{؀׿' wٲiF=΋uÅVK;֝6i3@X)));>ss=&Ҿoop*Dt׵N K+6%샤Z4sy. 7wI&黧,ޅ [n?;zȵKZC&|iOŹzuMol#8)+U3<Ƈ:6w̰IE/[&6). ]f]^o״]%aq:m奒 ]0UخqǾ.]Ŗl]V08Aw4eXde6NA9sٱc|Ͼ"ZS!p)ޅd F6rVo+5C憎6|+|QyfJx㍳gݻwBBBEEw}7o޼r7sƗm[<'F*QuX>'CH 9xƕ M=֢XRɾyy6Ccƌ ƍ힛A%쳤D=+$uRH%;uqĈN=s۷o?#3-XwkI0/XhOeR5Z-u-Zcpq~z4***뮖-[^|7n\ZZ_rzU!c|*uq iJm0s#%=I%B_SG &?$..w—Qyŋy!1!N-+X}wE  QhB-+ 4K+e8rLt2cn]֪x5gͰU)+2׳[hpRRk L1JΦ E`-!NŶe%\eF4zT:eŚn/m+;64S<&ےU$Mg*6%E׷S ()~dz cس9 #se} *芨Vp)#3s.j]f4ߑwj5JZn)Ȧ'ޒ^*eon婩[@CPq)qxbl3F֪ l!̪1FD/lXevڋl)Zfr~6K^HL߷A[ueZ0ږkUM'\J-+HpjR&L' {H;.Lچn,Yh7Be/ĩXa\wj-/(Fҵ=| l1OUƵʨ[@+W©Lmy{!+('n~/HOˡw-N"fs1&ӵ{ ? hQ]6gze+XRRB8s1iBp*vMqOp9UH V ֡ aLBiº?8v{6NINIb(E0¯Ap b(C9' dx((1"x @ARrxG$ 'pյ?D @@ "\eEuPF 28D䀫!"rUF^Tj9*#/k*rWؑY 㭛 @ "u`H/Mѥ!S&**`w@ "t`z%8>D_|c}rߠ( :0yA@85s 1O߳fl a$Ðb4ĢjuO,w"r@&/h Aa Nl  S&FX&N@ DL^@"@8=17zMg{~"@D$B)| —B DL^@"aeP\$@DY\^ @"4卯[a{@Q89DC D9Dj$*'Q* <D7z*xdggx~9]k6SSS: C,'XjՠAt5ɓ'kjj @u@ "YN Abڴig@5"fqD8@H'^sjZP)!n$0@,1?-&Iݖ_rhC%Fxƶ+Qvy B Z&gmwҕF}^(hqL DiX#i 4k.F=q] N Ξ=}=z4e2C!/[Өmfr^+?{#]1)Y$g`;|/DI{$b6a2]M()F +Q, y \ 29;"6?apa 5 D@;vO8q0ڵknݺ\ jn큣;K 7رcb﨨 k@Gf`fC3~2$:}-Yh*-F2 pbYa_pqaa⣏>-|b=B.~c=&w{$%I=W^im5jD큪^WE5W@s b{I4ww@ i0.\d! u~ a@ MftگӮQr7C \4eF#wE|bPi8tPZZZaaa޽oUT^Xs}E|@xX pm;\M @8^3~uM@ +Q@L:UZAeeG}4`zZV]s5 ڂ@[@`%!k/ɏ$ʇ~ ă>(w]j$88vG{[q, # oeOse\5@uXǻ0C5AᡂTr$} ɓ=(\ԳBۧj}M8;ַlzH&%WsDT,P]C :按ٳg\Y7.166awOC lnXZGZWji= HbҤIm%=B|?'iY>m ȑ#5ʳUmq\ [&M"ejjTׁEktp ^sf9l&L \ - R`_ˈDܺ"q˦\| Ą <8IwSGo}u(|[N]<[Gkme&dV{&uޱc'ÇWK7XFuX!w6l~O =w-> 2}3BzMC~Ծ)쫳toŕ)(3~u w!@}X!@(;La=L1fO91޾twZ]yL43]w/!?XWKwݻ9uS}94Մ7έ0y z]@F q.^Y\\0l1)$?)LmFr=2sa?P.7j+IBE`pI#`Di4R>v,sW;gw%##FO~?rie6y̬}zr/=M`SZ߹pqgkՉVi]&y{ uضQǧQ[=6m7XI mbWkmW0jRkd2LoG  2?Hq~ /[6h2ʆ¿xS;ݞ_IO'+漭uC>Ĭ_I{( _~%'wu/[&Xqn%(BeQitB0*TA2.4(Q/"sk Ɓ䕄 [/x @"dACq}y ;3ۛ 粇xO&̿7@y!yցUu&A@D @F <+(eyn/@lߣpy.8^3~#0ne8 6"@HMhf ;_bQG$}aq#LyPP!5 P-2c9Ll`]m*8#&607 &[.}*i9ٖiԋH Y xp H'V@D @1Z@{F>.H4@(; aXs ." dH! R&C؂q UρqU x0rr.l2Kr߽d"M^Fudn+茮t PP"xcNcmw L,3c=,ܘ<ǘaD}ó@X/A [r-8C _($@D!F : g&K茧;< ?H!DUL Puu(Q ov}^;&‚+8Vx$6}n*c!:@Dρ0e2!|fk u w!2$JE )]I@(] :z)Mu(bpBlD?Eэ@8<Ý.q $uvM፝e'~ $S z5hPh4fm1^)\k@pqދ=PKmm0Qk;jc.\H#\k)ko15s} "ARBXbc @8M 8Cqsy@ QBj 2O/jrB*hg_YYnmb)LDr+sG%V8289r#V9 :N D<Ž$[\>Jä]eO(<o&..]@ !#2s΅Eҡj[je0[VV.[`Q]]@nm@VhV\RwlaF&G֬!AG+~izP  w  s 1$fS.`L9C{] [49*Y@'sܹ/ρ(\Tqgbo>ZmŨ՟nԵ=uM9ly>I%GR[MyZS.\?r݊)K0;0L3;欢l锒e0xo.ĀcLwN6 밅8GvQqч@S Ĝ9sv.UOm<3hGr1/<9)į_O|/+}#hWb*hwuf.B?r[߾GR(Ѧ<{ĉ%KD@? 6T@H?#GBup7zMxR0~҃X%!#@ *l`Μ9*" 1 WRD fc :28 2@&iq7OLkzK-ycO4 ~˾M؆";A>.1wER@|% DT@sN .7”+O.F L!M3!{0ƀI'.hŜ.)++xԦ)[4qn6:|O\1kf){V3Ɋ=O”yf3M4oB P@d$gf9&7"37@'ӧO˗/ɴwզ1%^_\Z? r&|#qn78YĸO]d?ӛ')Gi.) K 7PXXr(UL *-݊ `uh7@'O<]tǭ[uxWoz3Oy.[ܭue˖T ~(B5K< @ dS_x L\WzMS dǧ=/ Ov" /}2S٭[.((xw#"UWx $J x€@pAE*ESwdL^J/bQm# w6kΟIW RN y䑛oԩSrW'4t^f "`pߩ(: BFe(33sҤI]v7XO8 #Ez={v6mK()((x%K *Bu w Bߑ@ ߿vϟeZ^oơC7qZ W]aSUBߑ@H_20P@P$JD Pw(JJN&&$"0@榤@ GJKh[ O!wt LJ@D t`p)--Ѷژ Zϟ @@Du`C^0x贈*Y@@ t`S (D䀫D9*#/h 82@D HN-L 2S (D䀫D9>'B8A` EaD9HN-L>,xD| a$P&<#O9 oAj@"pjeyI@o F\ x3!we!w52?A7IENDB`guidata-1.7.4/doc/images/screenshots/activable_dataset.png0000666000000000000000000001701112604157370022367 0ustar rootrootPNG  IHDR,sRGBgAMA a cHRMz&u0`:pQ<tEXtSoftwarePaint.NET v3.5.4>vbIDATx^흋sUdzU8jQDb"V,KD,AE^h# fヒuEvMf'P ∀N++Bfݧ}u{w}ww>{]]]Bӧid/4Zҝw9bĈ ,DH+ Z q2?>A~%@ZY] C58;~O,`$3 gP" Ha 8yqqzd9Hln0 Jdx-~&L0<4vXNƍh%s 7^Ԏ,0d?C/ŞƏ(>V̞roգlxLj>Z- S" $`ۂNihg=I'X2sk_ͯ)в"vG)&D^xᅊ}uuuǎ着/^X2Ck?3Vh솓 ?tWʬKfY<В4quXeB(ܓik׮mhhhkk;}tsssSd9۷oDžhiECk`K}]K7N:^+j\0񥉍 &?~Eվ<袼f/Α|Zs Zq!Lpϭ >yu9Dp$E -<&K.l>|y#|J}żѹ 4a(eV^pcAoO8q\Mrx޲u~8o@ ˖-ŶtN -< '> m6Y mϽ 7=pG~XQ&(O;w{րxk_ׯ,,[?_rL N73<#[7NZ6-0a 80[fzVH  F $jkkjhD8i/]D108vA}B(xӡCq79{.^@]#G G pmǾMWWߒyv(S>̯>F_ $s pxg/G+B'g8v߭綔>Zq!Lsë _ 6pҗ/__P}ڵWkK.Hl8䵽*="+H.k ".ϼ%qJ{PjTez$=sVmSzϢ} npk[’OpHׯ_C ϝ;y|I۶[oQ2mCY, ףWR+W-(e;+-UY*BƵcRm0ͮ l}N8_<>3Ere9v۩;89V\nV |ڵ $9r]w݅P{РASa M?3@Hjr@UڻaC#m !soI/E>nY9Pe^ܴar>\x+%|ԩSіIɍ1-~2N|G~V\n =SZZo"0`r<şO{c3'wZ +كeg5b>9)Ք=zEQ:Y%xd娢 TcTv\Pդc2 mxpr NߪА[T˜0&乁wb7 Db;x<,:!Ж'7WjV0ѶQmhhӼ_LQfB(`7Sn=nMAylEEEX5ƚ mpr݅Kk_:ܒ)gL=t%ϵ,8GYD}B(xB, cyӋ ЖfOoot⎲*Kmʱt"9rR[/>Zq!L nӔX+1J, Jϙ|rrW޿?wV_L^-B 7Q" m<+_Ѳ~q:m4B(YYh2I4{3Y@ f E28; Y M- 4;v ܄ ZR&-Ԓb71 ni#i-@pK;M Hk[ک%nb@Z {J:VGOf*O[D/;kTƄfɺF@#IpHlRi, +DٶU.Üڪ&lD:ƶ17s%h0W_9wk^zX2x0z"ssf46ZNq$fil[E٣)ڞZZvc}f~{bCIPb]'7&*=F7Ivr.TSF-93bao{[s.ejmZimʫAX]r%b V ӬmL  <Ȕڰx+ Y7X9Rn޵A;9W@:On^_V63K k34ϙQ #ν=Ks\/޽v* g5 O=[)ч%&i _D jPcq.=z[}FH%,q8c3$VCu+t?<ٌ0ѩ th)Hٺވ:D9NAed|jЋ)VQS}|$~v]3so\).BE"%fUM7$/CBX7 n4uj>z3r!@p Ip7iCpay0rHu$C:aP,TG; 0rHu$C:aP,TDŽ~,RӒmDSmmm]]]CCCSSөSΜ9¾wH-nd_x7 9!DR>AvggB^308b BfhD&3l;4Php;وFAkע[TIa5{ kF˛hC .u-ukB߆4^Yj uz+K.'Q+H7nDKm 4hů#e ZvrV&wo߈1:fTi ϩSw[Juo,%+^)BaI`p[X駟f#|:М]׺TO_N'B&2C,<=Spg֩[qΌ߷.ETEn[|g'_~nrvp++굝źZ(wd~%2Cx7^FlG[LۉJa"n"q 9XKڣ@l.aNyL`4Ѱ}SF+BIJpWփ[m%#{,^3v1)bZکw$@XLBQ {<xNnow1{z&ApGn3mO;%4A\,@pZvjI1SKĀ ZR&-Ԓbi{jW p5ӑϐbJwD=B[d"*)>4<`GY5I S+ᦵ@ffi"w5;;ʵN\.ʥ<tEKB'3ڙv4(ñ);?iid}_P`>ɪkT|SiyD9^VMk&_dcj!ShhFUe9[A@HE#s ŒF CI@ ॑,Q XBъasOtaڕuF|=7 մ EߵnAd-(ܢ|CuÅm1 hBvz"r~"aDmTwtENi@Mp'OJ?էGcWN_⤈;VwIkHw(SЦ4"A6%)b;E&nmJSi7= "^B2M߀a'SK*ܴ Z7-;);NNNeB{lXj  w;NkD7ETUQ@^YTݻ%@R^UaTM`)9f7pۺîz]FZ$xgǩn@'e@pM^qv9o0My68Eܓ_;NitGs7ӂ2q. w ;N;rFG, f)H ̴N1- ڴp&@X{V!W<,T! C;+Np?ǡՐ˯8-VC;S/iwYW pM OM KԴ>*P ~(7i!&-Ԧs t;8cy̾)M|ewÝvwT֛ zǩ_j۳mٳg ۡ +kӧw;N)4S6=-{hw;Ny ÎSfO+&v%0`rgp 3j۰Sf4a݅W0IqʋlY>;g{H:ܾ8XDXP*ڲWVXSF_DNUr`)IO8&p!ʡ SlfsZpGMVr*ЎSF7ai-@pK;6 Zr4څ0jf#>H"jHBk;S/sZ Nqh5$C;+Np?ݫa[[[G b#EpG4UHu !6& Y._0Eۯ;~@ɩ{7^ý*d jB$-Q/-@p[RXUܮExA-c(U)蘣4(1B6:4^5Xȹi;m*YCmzZ7-#k^/[[[c4~4 C}((*snӦfmpJmhn l2\ 5;oo~)>]_:u4,eJۦ֗2t2i ]"5JrOo[Բy\olق%\zFЋ]dJШlo7mڄ_ʤ5tFЋV+?.sj [\:I+8ѣJzћ8 s)))kܹno0+eLM,>qF&S~=nZC/_s̛}IH*ZNY1b3TBc/f$!h;gn_HBRw*  ܾF.%Z|98gJdi,`(d% d_,r͂DIENDB`guidata-1.7.4/doc/images/screenshots/__init__.png0000666000000000000000000013027612604157370020500 0ustar rootrootPNG  IHDR(zLIDATx |Un^-᥼,EhR( V^m҈zz%\}4mRQf"Rlz/QA4\^RP%my̜y93sfv6l6Gf9g|ΞJӄ# :;;?CxQ:Ϟ=;]O)Ç;wYg}l޼y3::J)WPu B]TӖI( )i!~,QayXڞ_f5|fr'oڴI_=Fbpxhh.jll̲nL&9s> }dHhW8<SԹr&Xxl.N>ve8S iŴhtѢEEEET:oof'[oEѣXZZJN<fgٴd i^E"B(D5 x֗X& j\x+|4e˖͞={tttdd$Kߎ~O ڵ:8~7k,LK.a&_իh:QɢJD_Sc"$]JS+66*#y ^Y&38Q>OTJJJΑ=^>3ڻw]wuȑ ;gy_֝{uf"]TݷoO \qs/ի/7&ğp/jvg}UW]Eu|Ο?kJ S-ZR /̂>A(vM5ekg~W=X{&Tz:$eg[h[Y]_lN.)b,F45ċMcϸ馛*++GGGQ{g…s`cƿy [omW6Rhnd(UMl|d޽O:sZx='~f"Fm ~iPQŠc,Yge׋/TVV~yſ?5o'΋՜UrRce%KB!jf_{zzz~}GgU{]sV﫜~'xm # GR? b"tqԙ (K&=?qLw- ?_iӓ6s {{.~8%\"uUWWSR7%ܹg=%K6^qs̱EL\uw{G?xps>y޷:f̭MlymJ{OoٛSYY?'ۿ ?OSE__ycמ_s t/oYe̅s|GX?7Z2g0W'!i}ޯūkw\79Ck~;DtY>l|HamggϞlw+̚5˞ɯ~N;Z2yŎbga/qI蘳/> $^O<孷ZVVVZZJMm6e0A)poDp cSS͘1Zew<{,^ T)%\\{)c׽s WMo%GjkvҏP>-oGO}cY)?Wޝ÷ٶEK^B>zcϝgXƲk.2)UJU6j| =_|՗^YwsƂ9\O> >A_l?|Kښ(wڵ+.]1w\{&N;'4-_p􃍯8yGWiOe2x~CFZअ 9=xݩmR*rm폴^Gkj7*Ttyx'/{j&մwY%#="V뿪y=Oz7+mP>^ /~sQz@#PP9묳'^/L|&YsUGħ?պ^=8zP+c:5?qC~_YjPYYyUW]pl*WGXOwߝJo\$*^PFz]\_J_|Ԗ(:ڟ>{'~ye`s_\[Bs1\N7 ./Oam͏4=Ejry2#7o8^>|k};=E˙s=\o~kَ'G."go3Yze.y`a駞uJ>=v].j=naKz߿CL<=E]dHghD]K&ˢ{;(x'Km{xq]SM_ҩ*tzttꫯŦKefh{tf MbV¶V̞}By)!Gv,wt#޸;^.B>_2>sDroWwȔk܋YlѲ}¬aۻq4åY[Al .Ѳg[(T-[mO#)_YY}{D%TuN_5ν˖} wxස9xw?B*O>2a|S2v،39RT_S]"۾rU%b+`߿b ݙCi֭z/aN2y~b##;\)&cʼҍ!j]$^pqg޻j+ $)F?cGS*+;vq$_=!ͧz꣏>I&/?ZVZYJᱱPZSKepל%goqC6ot#>|\0I3s۱cٳsIbŇO]4XR>)$Q.~)z*_ᣭKl +4?yЍ RZZ_d:#w~=Ln`/Qӡ 0ݐ%XXfg} 3!^>`- ;g͚EUI&EEE4}d¸ Ot#, ^*^K.]f P̛7WKK!^t7tŀ/.철wx^:qqFod۷qַ7 C2ׅ^HAWdDze ^@95wm}e -x]&A|:T:D$wq8 k@ [F\ne/LdGӅM;M5UX} /-6r_Mc a)kx%BdcG7E iim)FCo\A)5D$'QXغw5.5L};jt/VX1$kiT% w7JW`Gͪ^pwVz\c,%ՐD MAv4QQdbFT.6Ph^q2.he*]=s]P'26-hI%xx1^KDy{&8.^+c/0'EXn%ht,*E+`B [E8fm0'q;+j%NԽ}ըu#:b87fYb1ZP~(+Ӏ.%fF.D/} }eM Yicu,:ci2c̈́R ; uײӫ^I-~TLﲮFQj:%nм d.#^o]~PWݗn| &-F\rILq0s}8U?gTYċw Eª%ЙB2>= *9x2HX ǓG/tLpmr);z?|ۋi_"k"#mo+ dkpgdY)y%d.x=euJJ>^75-"^wB"^2T/q߲:=F-mw/p2#pbl&Wq]iV^ս=nj|^mCx T#x]=0N!^Z9X$ۋ_J8`K8H@P #f6e|>Z Ud+^]-3m*IxX֟8}}o˭S3dSdfr%1&L#Շ%xk5tcL3/u^/d/xy[ensWx靆̮>GQW# @~VQx{MxQju8ICw/%bNBGo.% \>{50.\o/#[RvpC98aET' ^9%75A@.OU%{T"%::xi,9M36֬!;*<9G IZsS7p2%`1) ܈)nLexoձ74xU'-sUUJ'\M,)JVA"gTxxKċ RA,(cZq1/XsKrtM4yz|K.R!Ňx(^4ܲ/M~ŋxwA`ȑxiZw2:cnj`L1Fa2ɚ[.+}G~@6#ޥ!7]hXY>* cᑱ޽ws²3Jn `uċ͂՘ʲ}Lu[`RRDJΉ0{vVK6:vS7}1h[!DMWUvo:vզa鞷jnAzW2WfqGݛV .9WTRv\<69Z$[5񣻲/K ilf uQwP͝3縊E"EayЌ%sų-ml" g/ӊeEĬ[Zt ٽJV F~kV{Yj'|W^vZY<e G`K7ԻEiIӖ)E]J8*EBkϡY3J.^2oΌP:-o ɾ}޽ၑ3ܷR60:h?Y;#Gwe#eedppx޾kޛ4UYG򴖭[:˓";Gw#U9աD>_ 0S'NLKakiwuVҫǢS~ݛjhځDrM^a"N~3ȡx[{+9܍xUQ18/_HsgFNR9;ztxϞ#^S3ҳ {eՠgo1z0![Ǝ&O2ːlE|KPCR,9D[ԭvW+PWImUŬA*7ej70 ^z8L'6!~⅟3ɡx%ܻhc?6MnƴU5[v4od>O82!&^=?mо߼sMC-EtuN^DbX9e ^ݶ xP^xwɒ% [rg2߹vm{JO7zם<P ݷG^X~`kO}T`]WzH*qm ST^$Fq‚LGUrCrݢkcXh5<ڪ,^ oډ8dP~۹wYKOY6sl4$w6d;xSk'YN?1u#1AQzdiK` tk:!U?,@ ^@ Lxt2(]x@@ȑx2%ܦlT5oc6TcJx4Qfj5JF$&x.?'~!!'e,#Ka CVƔ.6R=*.g [x\U#絉R]( b^/Hz>w%B "?a}n۶xlUH!x />d&>dExUSZV$b.*^]/ rըډT/Gmyw3iڲdO(OS%Hٺ~L{D#ɫN`31/ r4^[q1TZKJ[QRXe]`{cF}w.1ވYs⸱Կ]5]% )>sӻsFSRqWAQeJNj4mI۶5D|gټ|ygÛ&r͝_['5,۵f螧)x$!u-8]?/?PvNϑW&S5[H]K YTN`Q4|l=L%%佊&/ͪyIj|{]L-j15deSgirM5D;<JŔ;4|ѡt8$q!v9}`:9ؔ})^b=QۤsA3g;GU+d!n.ϻ ,HZ>Kh9Fi,>/BF:S<.&K&ԍ ^(@?(Wc;#o%[_tۄ UD]fqˋNzjNx9Rup_!:#uǡeUvJPGx>2p炜λ2tx۳ͳx9]  0_+.N@bRrn:Lz Lq2~|wbb>}c2ҳjqNF(߶,; b |%U<ŷ~\Ϙmo4Դ:8tz:5]WWqmjnns<ċJ#yjyO/j4VxLD[uű"$KkҶhNe(AUЏP7LĂS 2$Jg'GdkbYI,k]?hc"/(^![.R MVT"sO77H,#߲?=&,^,Eū0 CNCc,j/^p%)[W# hz{We +d[4-ASx k)qRޑS$e/xcioW5Qɋ%O / c Zrʳ@~Ψ!H҂b[$WQB~D5i v[6òy6_=.a/o4^]yTtdx ;{he.KX-qkq69&]*GqEM-3eeT׸tQ8/bެjkk#%!HD]`OuR&FCCSgljf-x;>j#q<4]ƯքvhdkξYTfWet8qFr8:ѱNGVGMX=*Ұ0ݕ D>q_ƈnZj.j%TS @12XOb2=֔aYk,[B9I]׶2bkKZU3-VWڢ)Iҋ4B>:t(]e_ɦlgM=/Jk#tλkAփ=ג94+ ]#"/lعAS_#)3r.d wLxS^tY#C*r]5O/\\IKVVF;>ZNR581=Խc9}Io۳ !<ޛ6:dFԞ٪m9q bpq bd6λGri%ǯ:mO1yG+M$uSB:[2J$ɘԲOM#^Mkŋ{$I]*-*g}{9_7kTDPѺfRz>9%jr2~24">]Nn3dzܘLrn%(lrHvT ^"Yn`UFkG>ɽ C,Lm2ߦ{醞aщY|^@/R5/?8GVwݫ.ѩ,F)Uײff$l8. tϻcWu:GF{jcג94+Dx]NOͭ<_ JˡA'KV,^Χ!\w5,w-ETE]`ʋr~obp6m F`rSZF-mZIc6T?ٛ .@9Ӧ>6꣗̃Dyzm:qQ|R$IYmM)< @vp Z{\K'N]o8)F؏؇^rNGb0SթA}V:=UmZ9SLMTTP)x'pm uQw倩-^Ӛ' jRұ4 bLx]0=y%I6L_Z{O?ÔM@E }M!)I ;`:1A *b8IŬ/n=7_70mTi¯Xq!fw+1w@@2⅟3'^`x@@xMG ^@ Lxg ī`A~L^F ^̴l1cD VzҲ`nw{ iȅml|}w*ݴfc͟H\eZ8udXDacUtn7ˠβDj}dN "?H[v1]ٕ(e>Gٖ3\KfVnY6ϑ͑* niڄk9\Eer,u ZxŻdoSA* n 2,&̧QyiK:UYi&ٷVnR\T-<%r"x}IJ33W4/-k^z~at]P:1TbmF wJtFn/r|NzRO[MY(S_h2S/SANE3M"R9+"ld,:}T5Ϭʖϑ4OX4$Eyz?"f6'drhJY^ӊWu"L }6}/F6Wcښ,j9ӄ_|9'}i3W/"&"pި=n,MK:djUfĻJ.,sIYg~pwsr/m6Fyyr{A#O#Q8UNgќZfǫN %>Wsܪ~-'R,>Gz/-N}A#r: λS| Aˤ="^|cB]TwM/6ʋs+3Y|<šx,b*U6:vҙnzbr]pi8OiwaAZL!ѱ ,9^uv-5t:UJ"Z]POw$*QNgG>Ϊz /Ea r8L'VP:l䊗Ⱥi_x fk&h.Wwt)q2ûxgoKiy r.N)H[1:^GPS}?>姇?CR#ݻ h^IojZ.ߠmtSb $6 OJoLVaxb4׆etD뇔E QՌ6sKAy9{>b&N~5]~Ψ 3仪Ey O8r}( ^2*/ x t/wGO_.aR+IM3e/  ^9@5M$$eJRcR6C70UԘTWK^0*16+)W&D}4^{m Bī:meE]PaI;xN˾ĐSċԘWuxsW\Ea.{($Gk5z/>o˷K4#/7Ͳo1:Uޕx7˩2HgaȐ^/K>-r~Ěl8~@N#Bޥxͽ0>GvddCĸ=RIr`cqmևWIZG${OO VJmH莌sV?x`ɧ T''*؟3@zx3޼|L ^ U)ikp515jeOi.KDߢnv G ^8A˖9sWt_ͺmfQ˴E!>d%)䐛Xfx OsO*L/f*i PUx9ť'^r, >L!^JVh]o ocFZj<_C6t5#Ղ@_[E%.*L/KDkm1=:F]d Dq݋!y4ǔZ" 2 t8B  *L ^@ @#/  ^ tk:5x@@xMG ^@ @#/  ^ t$}4@~xMGr+^^zi*v'6}=SUUUt)}x/#T$IZli^۾}0 xau{I|=@]Sax\F6to3aW1jiOo Bx^\OSuKqJ\DRy訍9J}^H41&`kppPx?|?Ò +^WTv]uڪln*xII)#OדM-j#Vgk, h&$a Z&] eWTŧ?> :%~Dݷwܣ;NhQǢ >bK鐷%Շ$5+*/)!deQLxœ˄ ՃoM+Jh]CYTPW$p#Lxy.^&Q b߳QJeK˞DU)F3֍'Zm09Q4V,_8EEH-VS"^bD;w~_~6~o_)\ziLn|B7nIAsiR-de9^eN)..ʀ3$4*cd/-KR^%^ lme9D<+x}-t*_ox-^zOIpxyS"Yv5@A_/[wC/I'5I(W:jSv+ܿFZ/a fըSƇqJL<@/^)!^@Lj\ߩeTB/Y\V#^1m|pظ6UHI~pܳ2&{Wȳ" I]<>>V'$zz#Dp3 K>Dī4q|G/iZCx  K.dtt=eQQSO=V ^G؄Q/ 6 KWm5W=5v!]x];xū> x ^/w5:aQq /  xeF\/  xxKFmݺ.bl7+Y_O5E׸/KF%2~4U$]/P}r8}MGc_bYjIQ[cct*}X )/"f)kRp=|\V1"I"^KZw0.q5toa|HR^2D2bxgS̽Z{J6!~ ^/ژ?́-ɱ4Ff1rz!Y_OZҍ/D%/X؂d^ŋP D2L q e$} 9/Ek楈. ‡x}RFuo/-ҔIjn/9֛H)J݌}|tbYj/jlݺn xs/jzJMUQ\W6/}i))cj%x¼s}]M1ED;ƍxɽkc;QE1%Syg^f<\-]vcX#W~엾xx1W i7e= Up5 x@@_ ^G ^//#/  xD(^###ZV:ׯXYU^Q/emx} u!mmQ&2u](?̃d3wA|cڨD{e?x)i=M+K y<@_yŋ- Q"^ y=y٢דזVBDy҇q*-=&'4Qzdjhta=ʫ9$Z!JJw(S빙\YMK\IىP\NWB)ƞ^37Sm29NZ+ SK o[[ĥz/ݘܒY)"z]3""W'(U)xYK7?<7J5t\O0r͏mSˋ1FQWc ,^ֈW>d}dSKވYbmVx ͦ=q?Eto݅4wʭxes=B&Kh׮Ƹ׼y`J~ TVY~˨/W=5e)"YMV걖}wԦb$zp efk=KBr NZ4A~,#8񚴏 SK/ce6 Cg e`3;S@:mR-ִ.<:.|Ą R*$<;V'XkGy8O{1z:AϖTGG҅1Oq+Yޤ'1RO+#?-zJ7GO]OPخ:Tf>ݧЏT' Z*??2?/j`҄ A~ ]ZLV#  x<~xx@@_A֯T&Q5͡j2v-VK ';)i2)hHzj}{ xb$3 v#j=)'2V^x.o'Ng|WSD_/'LUCW4?!<åaL'dKWL\$,8ЃC杰 Q0@I.^lUxK۫Cʳ8x,OzOuRbPB6+5T鱆LyҢ$kqM qSZ_Jr|gzdl3a'&TO@~Qk zJv$!*^h#G,^FV >DHEC/)F[b6 U%(A)UƮ;Q*oEUҎQo@~~ Z ]ŵOK3^}T^,oMDW|rRwF2WHzz5Ur/k8'e9jjAm9rP#ajGEqEܖ|_U= zWcj 2iV4MX[{kR8]Čxw9EXBJ/nbM %1^+$Hd]u[TZB!y0S\{˝\JW9aGWXcXZS˛$nw4]0^ffʋRFZ1`RC<,)[-ꊓzIR2U51{yk!D ^/y T2n3q9GlX ^//#/  x<~x"/(^lYƺ ƺ@XѺ ߀xLx_QuKw՝Vm-}a]2' ^/hY S6 ܪA/ %o @~xxx@~ V:ׯ} z @~_5&6Gmw@_0*  xD(^###Zjxqxp"^ /#.! K.СC)̙SOA*^pxӦM-rI 8~x/#{)--^Eb,%Ix(^mk+׶Sm%BI*Ht4&C$QQ}ɦdMNE$z:աv"5?q!+[ɕvN]O+o)7m,'bn})?xL]tWiYQ!GTF퓘l7ӠzG2B5E״P$:wӵWחwd5v("Irw\jdKwK\ܟ<%szNxAiܑ}@zRUΦ%OELFSbxVFNRG=zEoI+ͩ%A*bmL{ۿ]84?VZ]IRn%ê*G-+lv4wAS5q֓BV;~x݀-/WM/AyDEn$z{?G?6CN.c#W"I&i&݅|yKTy_^K)v =*<? \l#RGwӗal\̶T$: `oٮ:#'|Ѻ| ^/y-^ ;7H|Jr%S$Mm ٬(.[!BWҾ]%{{Y{uQW-=.5VoX֐)OZd irMEwLiy*;)whr;veeҡt8=8vXWϕ[nSZLzn&TXCxy;;p:qsD)]]pcAȲ ?r)Z?>λ͉/mM,ܸk@_Z;HW"+ &^7!}QƶڵaRJQv1}=bܤ箓Ox/#+Y.js~>O.CU"x [^|/#xcC^|\ZݎolMĵ41M-mW9rkX~:OPM=$n99߬(]!XTOq{? z-xR|w}w؈Fv꼋O %ū$秷7*owYG.K0Կxi5շK*^}hE`sd=5)#^ew"4Wd4wZدdg,^\y#^ˢt񉗸=%ty{+H?/Kw.pvu ^d>We]͠wCnrU􉲋 'AD""ᑋK5Zob % W! ΐwc U(]:RRt["":;]ZdTSn>%|S)'ASV1+zK%[ 4 F  Tis@x] wv(h JP4Gt@x@x] E .^"^@/@/ Хky6OI 9 lٲfat^Y>6TV,Uv n+Z!7<ӫȑ'C Х'׏?9:s6ؿc˚pr}`yG[jFsPMXt,B㊮64w$fsi ]6ZYAhv3rҍAr ."]<^j ^|(F!sٚs[fIn52FmV40,eW>MHAW|BŎ*eŚ&~2L&Z=Ǻ2cnTҖL$eNiLؠ&E(R&Scm@ E*\Y֖'-dBU1(Tdh9[Q[cH.\ջ(%N^/Kۅ~tMBu1WKB%>Ksp}%NOI)yzSs`{kU Lⳕ+]qU 9U\=ϤY-GYRxK L: .^k[c#{W0k[Yx o*6xj|;sƫ0̂$`i:a ÕJn++RZk%=3WVG \RcXA-+NRȪ-Ҽ;8 9Rn JGh<@ԅˬR.UMCš5h1^@Vq/P gQp4 ,T{tvr v{x 4XۍlCnIw[ִygS3c9N'{֏KMETEGfG,$9fThKc YxH&2d9Ro E- 2> RTS><U̕()b/ .8@Sian*nKAtLy |WA醲<+*/  P (t@x@x] E .-^qSRCWٲe« fZo!S4\G:"6oYS WGh^@V\?zG`dզ$QxYVOw$ð]bϸ^+8eQn&ӹ2 } R"9#AH i}Ex<q2QtQ94&>e/llCfӏ=qx3 fLǸvGc ٓIFe^g_5351# 4T 2iOE$pS+JĞbqK5gJb0pc1'tK1x3\z'#}3sܪAV$*ʑԑ%֖UbvD\J&>Z0JvF/{_[*;{Yi].cFK>9&RϪHj"fnbɼ4ʉ{H&]mM61)+>א$R4fQ:֮ BmfwVz7Qol0sD'Ms?ɔ`{I:L<`?'^@:hFв#2"+Ax] 0PmVC!*Zi뢓@x@x] E .^"^@/@/  P BqռUSưj?i$;8sB58  ʓGWLjܸjh?M3^ pةj@H/ڷ{Ƌ|@Yxqu>۲eMZaN ^2vlcCx| .^k[-c[ք>!t^@Cx4 .Uc4;T;f3^:/ P@x@x] E .^"^@/@/  P (tiJNuld/U]fc1 @tiwLqu>^[!^: /K{m5BxD@x].¥$ ^%qռMKӀtJނi@x]*67/C^: /K+bцhW4 /@x] E .^"^@/@/  P (tPxm\5oƤ-ll Y)V* w+M,ZVO|n.D:Vί*iPٔ4ʊtķZݖ/K+O]0ioZ?%^|w.a#o\,0-J\ F̎d_ͺiK[L .m6qiŪ JT6V}Kײ wo`|Gׅ}=I~u-X6TTF7ch•&i$"*3ɧ^ADxnᥞdэ;ތ>́RekqJWcEyy3F4-+ 8*v- >= jσ׼zrrL: .^֮ݸYhLAz«49P;۰lp}>ӹclkgfq$B[zM*]SA D2#R-5 !#ԞM1ijYʑp%_edS#||ydaRmQx[ 0 Wx۾SRTeRxeamHH(=ii'*t[J,p{7?kVRR2&#^@ ~Wb^Vzo f, U w:XKkIe3^YE^b8S%{U!|h  =^.>qᵊ@x", JɝCRWEqQdE厂dtNu%1LmQ\e<&Qt%^9Ji8uJN%x,W#l8o∿n`T\ÁWц..{7⛼B r˓wüE(guΟ$3M6oGu(~mZOfl4, Dk[`iJ\^-J /4-JӒ%>Tљ)13)J3cX-I[W0Èk7 my tZu,uĉٜ@tDSA/  P (t@x@x] E .-^a1KUZY)''^:[s; smmٌtg:`TwuXJA l]k&>m1EhֻiMJFXo՞ڲwХ'׏?( r3 r gj$cBeЀnFEԔGO3l27 걶KRmyhE^ܬiL- f[)UUxU r8밲c0۲ Д`wt]gc,7Fk벸O c6&uU`0 IIY*tzU O˶LEKԧ!Ȼ$1HfT_ADKT6ɢ#O:: xy/0Ae8x%=.Ց@x]:Bx组/W;(23+_+6|X(wHohڊ*o.<Y A($ǀrք]ʧ`[M /g*^Ց1*N.˘M#+EqeL8N$>'V\)6ީ~͚ ld7$C* H<6Ik^FMمL(Q@҉'K~yq}Y z˚+>ާm W TC!5>283k[M^wU3wN /:ITd*P]z\5YgQxհo+$5eUUxP 6pQ /i%D;W拥3^T=Nc/K'/Jw3^( ʙww5!9=H(+N聧<^1Ŋ#)*IBb6 ėN0UyqY#j9u'wɠ 3‹JM*%R#]}Ż3/q0@N'f ߯#}ܛ->M13z{^ؒA'j dW1 %K<ėrc'ڮ'RʊsF:Y3W2qVGtin *Q ^N[^~7޹ug gΟ$3ѾKs(Zצ%$ͷy%H'-@bsx8(̾Rɝᓽ)}lenosN'wTyjyfx:,H.I"NtmtaCi5 '?R,chM?AIJI'^RR񴜒5E5^juYȚn! J77D_A%/nut$֒M"V.Qn2F#+ċ%Sd6 N|y{getTᵈ}̤ByIt YetfGkj0pt6+Nu^@^SXq52'3C)v8MjuUPNI .^"^@/@/  P (tPxm\5oѩ#c 숟 /'fxqбܕ@bl'SŔ!?L .<~t .6-uضg׋fBy0UI4uuUѵ!O^@j4r5!"k!}gJ(e`Ca%:4X7jV{-ˎ?] 2϶ǪNӷw0IYP1L#g,ӏ{CC%gc642l%I}^i QJtFǽx&z 3Iޓa;|[(֨/Kds3p͖lOx־<|{eX(#xZidV\k溧lD]J餇%Ӗ0Hzͥ$ًp#Ӭ5*IճIUn|&OX܂ry+h9*"7\c$ ^mMKopƍ-63^$*wDL}܄='fM~l0\q囻uG֬ғLNV85E!q#kaHe>RXdjd0W7d'+Wx n\ݐ\jLآwr٣HCcU6TᯂYϙH'q-f/&j(dO*V^u{/*L!"@x]ڽ+T[r[dP\^,rP3[xM'<^1Ŋ^RU$\ĘgˣP|P;LF)E ǔO@(X6$Xr`I夞$q2D˛!=i/K+mr N|B #ń_f>]p1Q:csljIv9r]wO\u # c!W3z܏kLi MG WUZP>e\JLW1\HXD?MҶd'hs Ű  :k+ױh8ɞcP^@^#L@@x@x] E .^"^@/@/  P B8'54VLw…LUQb℻F*xU^MaL pR^񜙱Ũۮ6hJ$)xJ+%zYaީ񙹚WJ^_"m .\?EmMKopGW>/ƅ:Fusc4)-jAQxdBU`r_ 3j\<^1Ŋjq99*b)"&|ʲz\/RťJ4l[^x]2T);ˋa6O>: Zr@OХEYb\-;8<"r1B%Dyr[tD\8N¹'%d(4wKSjJRjOP N4kCΞ?snWs5#DZbz!V`Չ\Y fbu4qc}œri 9yxv)'7K|gc!*D)Kxgٙ'V}if1Pm-YHo#.Ij t#2v@gt@x@x] E .^"^@/@/  P BX \gz˒GCx< .44ڕW\k {=yl/ХKI{؂/ХkŃq`RtirboZCb^& ^@V /g#蚄b^& ^@Vn_۟w $ ",1ᨱmIG/@x] E .^"^@/@/  P (tPx96: lٲfaC$V۽4J+G}.3r>o'S!VciYDS$J8Mq8avD^FXrQ)uBl+MnuWGڇP&  r .44=k~b# /[9r f Kg3rԁpyWR NعQ"1_tV!_J /*5LWae#C;*'.-GYf# ХKW-!i*; r}:#Y,WY63hb4Xz aY$'9TPOx =nyo>x6ATJg" L`E7-P٢tR>q.X2I.ɐ`Њ,UxeCaӉjnB6͉LJӊi šb% 228«cv /‹,q74[r$t[2q .^.LN;fx/E#BϰgzP.o&6|bV+/`aE\Ə}lhAX(qZOLѬWOqjUIܢQ*$ƪܪ2s4TT^b}% e%9:Bmw%O Fu@Z2i .^F5 E:.^$K7gGN Ȓ#IWML'#p%s#8"GIxZfs=Zw'WPНU%kKRc.;WZ2i .\%Fѣ6b7:+/傥FH7\Ŕrм6Fq^` iJD {DV̵$YYBܭ c:Gd޳P*oI5&B /'%&0KE7>h PRtذoe&*xt['k! P[K*^@ /g5qe"?\>6 K0 gMi,r J#켳dap_sqsHѷ۸+>c?*acipB0 ۶S{%Ϥa͈(8ӒtJcO8ng#WI&k]][l`OOo/ՒY_~~J^cU VLPEg-9+0=ޏ;$aY]ėɕ-@x]pjɃ$QeR#@x{IuPA8Җz @1eK  P (t@x@x] E .^"^@ /8Fb-[,L(s&It>sSiYDNyɷ}"9bfږ'?d:5ik6YRbŢhtN˻Zzo^6!.44=K~8{^=ڴTm2I`8#q-H3榳`Q%JQc 9eq(iH3NȦ`h=G՟証א&ХKW-!a["(N&ل^xm[xK$|.+Vh8^6,XʕW̑,#4Fl.I#c/!}*S3e~*ߋ,꟰TYʹe"9Kɦ+|dN`V'ђjSjtg 102MH18 ,ӗ5TܿϳI˻z6>/45#^@ /GemZ#1XKV!`)V}yG1W,_{+ޗco*#u3E"-Zo~;fnX&Z Uߤd1by&!5V VNϠaeKldQG?J9@ȐQ(xYtX)^\qT^iyWϦܧ,%&Х5P].k'&w݈7LWYJ|K>`Hh$E+uۼy«BHh}ڛMRccWˤVUG6!.\?n~(Mxh獎](,5 g>o`!',vݩ#X ƢD$Ȝ#fT*y:ID%oR2YxY緤o"Ć]«BZʯTE#&fb*̫a r\tRճ)Y&HxB];y8\/.& 717nR+,֏!Ӭp_sqf~_ .=J[gₕaX-8>͡HXfϤa͈(4nt+⩓x^kΟ}!3IĚ"oWW. "{ŸdrQ-EOI@ rS#|BʠL$YGkG6!6n'%9qzť$OvVkjba޽{[۶m[l bUEj!ݻeI;^ez}k߾}^ 4 %Jx)_"rؤbds^{o $ }N6 @xk޽>g͚ EB/V]7|K]e^ P$^sޅ7.wK&Ax/@Px=c~:>-^{|^"~upx < hr,mt.gˌdB(xko..YϓN: djW[%m\x#-AxXjt ڶv`U^:Rx@x /Oi"q>۲e^:/KkpљZ@x]Z)k]Ưۂ?nAx4}͛!@VnOrQ/^nmΜ9>wugpb4VxFVS.; Z̙3ksϩ{>cYHcג%K6oߟm۶-[kw1Os;!Z]jf̘8 hz}_}5㦲ׯX  6lPӰui.=܄ߜw?o+.C= \x%-٘a ucͶͯM /ob'0{l|#NV}Odc -/K3޽{=>%k>y֬Y‹$lߺK^GS:-&a IUM)ٓ8^{v5xw8gΜo}['._PGryХK4z嵷z&f<^6~|Azb^Q@xKm/L9hrKMХm.0 `ZMÅג%K8P5ӧo޼__ݻ] >s̹{ct7\x4lAx]+8n˗Aڵ{B{'] >s=h|i:ꨣj  $ijwn h:^S/-@xME 5T h ^S/-@xME g#/ ՝*^&>iGakQb^6>s«;Ux@{>2c«;Cx@xu'^@՝@xWwt ^ Ё@xu'^@՝$f͚5sv'Cٷo޽8h(Β o{N{G0mڴe˖r)=\&S+Z΢ ZWT:.]:11w===6mzy` Lٮj+;bGNj^5Ns](6I{Ҍ/&2:Cï?xԟ=w/;g=|0~ (?:݋]M!ӧO߶mUW]#v$>p;x̓!])=(x_uح,$?&{;/Z5>/M&u[t Vkg\t}|Lw'՝->u'2#sGus#/oܿ~q،Ò!/JldfTg'\y9?bhG=EZ h1ŗ~iܓ6S B_Knj$/_^e {[♇͚`қ.菝E}fw/}:Q"Y(^I.gxz:w y3y9G8燇B2zO6_o|~qg{ɦM+&>oyރױk߯s^;DH -L?믿 P'ݕ<^c4u>{oKPg̳H6)D_cQLܺm/6d%'|isk9YkVOl]|}綑?+/>?Y#?hn%8uO}*޽sdh]~Y"~O~?T*7x#ٕ&^}y>}lCۧyo|4+!Y|$sz/J֜9s_@M*[~;v㫇ng{%`3Yx{_yg>߾ij˜CtFd :y;x~V/uv>EfU?|^n& $@+=s=q၃cwyu>k?I_@Ɲlwelk_…O[';7Ɲg1}^KQ寃bGN^ xꩧ[C:EW<5GU'h??b ]^>!^wt K$7P+N6vW̓yٻٟ'iXwf?ǽ!#_9u :g;Ν;wr5]Ks~z $@ŠZ,O?[o3_w {eӦsܹuo8{_XM5Ŏ՝!<̓N:Gmw;?1ulWBYO_DL~^{I.Ŏ՝*x]_ gzGwK Pdv%P+]Y;WwRx1wm?S_~9TJ/&tVuyOpԣY8 zNЧ:t_ .s׾N8ǎkmm>-''_~xz-E .>kODF 0@_"/4iΝ;3a6N=ԗ_~yƍc^wu---V߿nn?[ou͙*yyyoll삃6l+D%}JN:餓O>Y''|R .v/.e޳gϹEhmmE<|uH7?srix"eڼ"65xձmw?&LN{^ѣGs+ISĚ#Gj|wڵ?;œ"QKx^mW+yցG~gJ4G[Z餓N[\\\۷ ޟرCdcǎuv~֞~Z%+jRQK.KN{"Ghiikׂ(| '^J4"ăK]]q'N4i---m\-[y~> @osN\}\s'D^xLEp9S_~O)m"x[=RDEPO09"Ĵo _Z%q% .b/577Ȓ# cj?~ᇏ9 Od3<\:;cG-,,4hЧ~*ěowуKWQ \sM՝^'ϯCN.E$#}yw7Ȣu\x['_pC Do<"dz4kllYػ2w\߿qƧ~Z<-((1cRSSl2؀FU3}'ϣSZyT㺤'wJ4]FW3k֬#GDVc}=v忽qg4UK)//'J/W7_?[9U$]~AjtPsg?!O?էhؿ6sϝ3gζm-Z8p֭[O:$}݄N/;DS 6a9.|7H-{O~_nΥ?ԖMxO>d#ͽ_Szs8:kUAZwZ /NԀ/o{=U(?8qw=xAIV\sgj߾}cƌ9xC/\pxb}bAA2ϡE%U})AV=Y5c1ߠa#JV΋^zI ?!C|KO}zmr#Cyl1oܻwoO~yĉ?=2ߤ^t+oհO>ǕoǞ*_xɢ6}OOSw/<9wd|!兗,ne}RؤE^4)bk%@9v "yxToRZ?9o\[Lx->|xܸq}u8ϟ?_xxU [:[{Ҭf3_ټWDc .6lg3gN~~~~DIj@Sr葼>nQJ3"p-Zqg؊iO=uIhĵG>W=ZLJl}xF,~2~R-\(ϋ'*6~sg^&X7sm-=]LFpuY`KJJ{gzz[l)((<ʄaE|oO߽М]ڔa猿xӇZ%ɓ'ǃo~c.;yJ$KDjnnnjj1b+ǎOTUUTcjAu~ih⥏~Z'߾l7{1O~qe?ev\2tɘW;hs|/_<< lYjMXX?W)>rӷ(bD8sl#G7'X[o[ hjBqۧiQsf .W]uUӮZ8㌿'pV[}\vC{{鉦(힓g׿u:tE|_{P(yf;8m@$7ׯfo+^_~X"~ڑw\ˋD7IMe]j%ZگOSN]0bӏv~ۄ'N:gKE72o[۷.J&?"+j݈aLqtIUd ./BշtM"h.@p{?9O?tA9C>أ*P~'O=ᄼcwIQ/TsZfin'>^&iOlb^SL3T2TbhYdGl&Mz|?_#G|嗵;Jj~q{JWEI K {oM/?Ry]ݡew;ȑ#999ޓ8p+ .=\~~~ ]PwzW;|kid?"{s=m-J[GtEm;3Zm**.8z_wG{DJiJHizlpC]zC5]i#ܿhT\qC<'HmU9ؗ\uT/cVZ%n!\yL.;w:Pccoq1c%'bަD=ikzńvW$US<[ʊM[7?ywpڛԔKv˶F#*9|peDsM7Ń~Lo\@vSO=u̙"8p ӛ`NeժU"\x. k\@ ApYd 5. k\@ TVU_$TVTT6T,URfvBKaepSK淥n5R.EEEƧ[oU^=3ge[jR .ł.e-a zNK=gñgrK%WۗJEuZBHtOt=$Hgqߖ&.NaL¦Pxw2\2Isp ΟX A}X/6GdhrRpWbE([ ƖJ҆/2o[jPjbۣؿV*"R")LX-oZ>}~x5&|wJ"jVD1Drކ.Gpt;ivfs\W^k_5Ē}Vi|3 v)5Hcc`(Pi}N"KRcG}VєbW|Lpv5&(DWQu퉂u'UbF~wp$18W}`40¶c0:Ui3e &^uٮ!"SU|R|շ*MA\@@pYFFrvd鮵(@LZw)#zx;]{s oí'E*]c'n R.%EEv꫶2wƕ/kApI9-h{^L2=yVe0Uꎽ:jJ]z %].]IΦ .i-tn-.驳'pީҮ_ @IGp1_K^}Ⴖ򍑣qf*v5ڭ-z6R)St:jn>/+3rEWc?0/Xڡv0Zn('5oVmJ_|Gh7%~$=V~y_t5$xe*%:ݢ/i $*ɷq0-%WdxoP=;ӅSnI>Ď{(9& %'v7֛%~fuGG2eZ`Y(0E .b݉OThKno{ A>l)"3>]i?,conƪ 7. x$m߫9>uzSw{zۊ+to*n{jYwn^ [_dꦥb6$+SdFK/Bo~1 92?,);ctbk-'mwtx cK!`: .+?+訏Pں4r˜n9O &b8wNf_%Mw(MmWm{?ȤVx/6n>]l&%H}ߵӮʾ\wHKWQDw6tin*nINК:ey~.58:}#R)d6|#\hqܡ.ޢv'. .|""cuٰɳNсTRfUn`f;&YGgG*,Uқ{;lz%}pn,(%t"[pXKHr~̫+KoυP#k]k"Y|e4.7(N&tJ[L'\ M\'xjDL$cM Zmv&jw.5mۻU˛`\lܤ|7%>6?Q.6 ~jafVdue[,08w@Ɩ25i\L'~,a:}]ϧPӧ-i|s.p $wnVNw:[Pݕ} 'HSu\ %./gwvvpqrjr]RT>E\9p 5. k\@ ApYd \^o*Z"QPyʱʪ$n .-ew-PD" e&tY]JO .˺#Ѥ^;27g[{(?E Pɯ WMEO*"o)Z3>=0ZݤϮ&i|E[6o6#"NЋ5<3ɔRpT$HkI/ؿ!;&QX\šIQPxs+*J[pz};r'w]\n\sѸjцP&GnF\:)hAp@pYd 5. k\@ ApYd HE~~~^^^ګmnnnjjJ4R!Mwڼ^a 8BqMg`!,Z\eƍǸl} vgzQM]-'ojWdԨQwu6խX"+Kg!H)# d*N*R- KUͫ1Sim@0t,ibUd\nSW;{+K.&rJϡcnb- h&k~kpQO?UX&FHhgr{İWiR돷yhkէkU"SUlYX3:7]W4նA}U*Vxyq, .o/Rfz_u]wgj d+~NҕsT]b^c~} !̘1x޽Ć~{]pgqWiS R[[zj;~N%]h?梛bUjvK\;eϞ=Ħ7x?vN:Ǎ CG%*d2m4+H-v"E1bDMMc=Fpq/ +8nM.H'x^@Vp.rYgU__RqG~z){M )E}73&--.۶m{EH"4+,,{֚%K\@piӦ7ܹSrn<'O駟.GZ#͛7\,\vq\܋ (**(el~) ;S?yϖfpTVUʊBʆʱ-V]+a; Z^p .5ߠsLHy~xsnlmJ(ݪHUy#}pQ̙c|Ui,Dρڴ,NZyV!,OJ+*ԧ&xMdBdrYP7&-X"ܲPO<7j2uzM%\(0۰ߣڽz lm<Oi껦~LPtCp?1cC~m֗Otp_Kwsssm۶dh"aLI,*SG5S^0A4mpII(Pm9eT͖G􂒄th)qOL%=ڄm)^䙪;[g.?RGXy9.(Ҕx6m3LoxdmUSJ m`ѽiez-,/,kBoXVkܹs}>_kkq֏] w?{_:8%'''/[̽%J/"p F DB8^Q~#EŘi"O}i\:8%{*(1Ec18 [I\/4A8c&"3Vͷ9Ibi=JpўL+x̟T7܌YCL:NpxG-8X+%o)"2{qIc\]~vzVXRR:lڿK.rO *aWQE;v]E "u`Ik4?*SHWuAEcvJcI8ۮ"!s#.hItsǖݶI5 $v_gt_ZѢ@Ȣm[֌zhI[mWe~bW̑fGU'T"om 5 ._k+q0& ͂.i0e$y; N:.xOļƞN9eƌ{5zdrUu;3M2dHmmի{Cp14tQD\97+ sXcՁ軝whiXY!y"ٳ8K_sC?2tP\}\z+U eڴi~8k޹םPߔƸ1#m&KCCqoNЯ _rG[6ǎo*swDQQ.O<Ž.\n喳:=zoSO\KLoI .oy̘1RK mODpi&}WXX555K,!4`ӦM7nܹstx<ÇO}充---]\Ennݻ̙fK\Y!,]Td%ճg@?"=>WxEP<0;w7oŽ.\|O?] .G6+7o^uY"ر{%Ldo-TVUU2TVTT6TMn` .5ƏZ}]iG%nPݲ/=TLنLߥ-_3vWlqvbԨQүDpyzlؽk.[MG }o.jsop))* !=UjkAN%(« .II{rc"J /gN:kOuynކNX)Ɣt`ѣGKqYzw/POzhM4^~..%ec>-5#(UD5&\DpoE.b bwN3DĴE-'O:;ܼ .ݕ\.A%}EUR,*+Z+27"X .VՓ"cP(2hYsNHy~WgY$a 8>UCLUi,؜ӊ j֎_|ҲL=/^WTTOM4fȄ2Ψ&)iIb(eu=Kh5yLKm93ThὉh[ؾ.6lFd]V?7͊}Dpz?21cH?^Z۬/PʿFn۶mɒ%.}SĿz@I\,]EbzI(e 5(Z=ZZAZ"jцDp1f8 &'cmΉ/LU*r3o~Q;5iJ|-6'>vw\v 7mԄ&5b d=n5:ֹпRi{*(úϴ}rܹs}>_kkq֏] w?{_:8%'''/[|k $^ҩ%+kZКJ4q4JbȔȢI%HUXi1F4/^[lvty/͝`\4HeV Hcl_V9s ϠEhIOgCt9Tws@㔬 .>}I8}aJbH;K*]ErsڒҪ6%#'\,] .ֵO+x̿%РcI\nó!t}- ƞwP򎃋BQbQ*)\>!̞={ܸqzrEy޺+V>-}4j MPDB~2%QHUd^$, *cÆina*R .}$[0t2i6\?ɵ ܓdˆn{&Zp6DEWjxb_g?J|,XҚAG~.!Ⱥ%jr)s ˬYDp9xq7w_9ҡy="\2ۂKwZ/Pud(ZH|pn'(ε Ml#/F]iAp.a .9Gm3ѲOimpnK{TuY6\/-Ujc#Aӵ)E)~};+,omPTKFjۣF*[+b4˾Y:Jz]6+U)eD-Ogc* Is tUFɜNO${ކ쨿+.@\f̘Q\\w^G6+\Y Ϳ9Ӕ!C֮^^WM|ѸD%-'2u^u*+E`.>\9˝w)˞={Dp5g;T/S .CG%q"@Vp.ӦMYkν< =4eĈ555=Ž.\n6\~Vxw~+&>rC>v|W{LDpy'.Epdr-uY]\ѣ~z^"dzHmp~|cƌZ\RPTTm۶~Z$ H3B?=kjj,YBpi&M6nܸsNʹ)x<Ç} [ZZݻwϙ3g͚5"CpYtRKhgπ~JA?E{|:HRryaw9o<{\Y!<~\}3 ^k=lV6o޼sDpٱcǽKpq/ +8xH ."M7|N>[%KCCK8X2SYU]s.*+* **&ZuIpX ՍT׾i .5ߠJ.[$\DpDnP"W)3\ݢpyRy4\m|EMlzuUS=U Sa:sf'O}ɰaΛvj9:S. r. ,=zt5䯇Hz/^RVT;2Lp2HN) u B࢟{"&΁b"t'N%ЩY.YoBFwԙjIHEUVT6xNǛFcA'0ͨ3zI|)%F^8k;^Ĕ[Rm(\$b 9/Ŝ9WƂIXl,M[X;~|Beg2~4J}m7D&D(1u< zc)R- sC &\7Tr =Zk4esd^tCp?1cC~m֗Otp_Kwsssm۶dhEdM`Q'x>}A" -(Rbק(e5H\L'xp'X|ToQ2ѣM8EʺXXmW}pеxMycnۤךFEWdx;bysׯ3%^<:ouo]\f͚%־ʑKI-((eʕ ."ScH뗉M082>8.O.Bs_ l7O\sg~U04[?S?a{mpnnc5YW0l>_ZmZ@Ȣm[֌zו% #emPc_^^\1G:#wuk:\VVPa0M%=/]tBQ?{nԧ;:83fݻ8=W}֡‡~|4eȐ!W HEmʹkڏsXcՁ軝wh{mu@q.wy.{1לPO) :TG}7ފ{Cp6moll4Zwu9T71.#Fy.Epdrm`r3+\1іM:v?#QTT$O2N2սW呐8TV(p? Vw) "-0[oU^=)06 *}AfV)3g*)j@edICM^p .!\Bz[\"dNOK}")2t? V _E"zpp ݏc*emԩR. \S.Z\JBZ'(\nL0C/XK. V`Iu`q c(:LFFp]) }\bmЍ.Zn.@#k\\.Z@&K75. k\@ ApYCsh kq:@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.սW^]]`ʪ݅-. "d\gx\ SƗPYQVF TRfJdz/"kdp1eʀqW a=L#k`p7hM\ip .D/ \p**Sf8h:UvK=[_P2Tiap.2\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 \.Epdp- #k\\.Z@Fp"2 M$Dpd 5. k\@ ApY}eױʪ|5q@t_pi .8T' d8.8?ϭ\L=ۂ nf (? g=v=Kׇf*OP}WU6)UPftCN%KL/6 [6?{TQPLE:u=rh]?A;ҜUZ;JqԷ">pMʌ廚~S-} G>MՆvH=[,hD>'<+|ㅏswpu[IʕכV+73rıjl wZKLT%K]K ,ezVJ׵+MEr(t\ҋi$+KW,ۋ؅ 8 `sK= vIZp5k.&_;sڴ=-((eʕ!Y\2E(H'UFCLJ&=6\uUz#nz_pz2\1T:ua4TV~`':W^b{22EQK!cuՆ*iRlުzy_i;遐rD:^V ŊK;Pqщ97.8s%= "/|py_p ѴLNՃ"O)G`kvP%KcU˄l- LO4RRPp8v#ޔ@L,Jim+[odZ\" ՇoXf?#aKvq*o}YO~cpvpmkj%Q[o-rXj3SUK+f0[(Q>quqԊ*vi6rp7.8v'=`anHM.Wv8 75Q9[H;C "+߃\s㮐_mLÏ߸4o}Yڵk_z=]$N ڢGTèmõ1rkю`րv,+MfSJl[ȵn{"ޗo nkv:rp7.8KpO{]×[7 |pya4֚``uJ@m >lnv u0T04 Cs\bnM1}Bk ε}\%߸oP.خ2d}#'t8*QWqӀ3q Z4eiN@K2[aH qۂ1h.0ۂ&"KG185̠o"@E'_Wg2+$x]EGp,KL1k@\Lo.߄ܠ~.ezCpREp, d 2 d 2 2#???///677755e݋M6UWWرyaÆ &8p /^edHUTT7n]lذNIpAGpA_k3^ؚ2e4E|VX1x`)g߾}gϮ$$—"X5: \ _Ȫ!}mp>} [ZZݻwϙ3g͚5D/EYYzرbn8>3ſ!,]T|;m ;PwM3~tm8ܹs޼y ZvI>tXw}{xU-(UJ.Qt8+G3"U)@+Pt< R@RRPAE2-`һiJQ$]limm- ,8dɒݻ;%oua_ozQ?7m$~ao\ ˢE] UUC-Hv~lvlzhI-d;轢$TWWϝ;EhpQ:łܿiիW/]]s%٥oE[:ö<]ȶuVBBN8APpijmnWW-Gok6ڷ,m=2?>`{n4OZl@ ? e޼y{JKۏ٥سK1a{Ij! VVV.\E7lc3KحC"nDĖGTdFsbpC˥K]m۶. .ˆיVvKjGrApѿ3F*.%#zh>{W,W\H_2_e/$GO 8[\ס!8 .$\] \=oNnB C˜9sbbbd?mXOC/VuEu ;WZMZ8ǎ@px\h|f~| j3|qFXƽ%*9&q6+*#81D>s+ʍee7l$dg9\$sFF/*}.{O%έ p#Z }hp_c=F?c:!nܹ3 -Li/ǜpՙ•:6dz]\2szIviK'tCm':@vЊe˖=3;%rb8]w=Hk*l 6vWn*.4F \g\^)LIᶉ3{F(l BȺ|}F$* G9mhpIIItX|XҞ'n0`KtACWh"B3K]3fgi tJ!. =p/rcLK2zTE/чslApMUs>.Kܞa˲Wt{8z .\hdkb|qx_qQ*̀yh ;ԩSm6ۅ !u7|uQ7^|)w<kKQΗڷo_ZZf&:Vs5d¹g5řݓtQtAp)82yd\Ν;GIjN}ARˉ\OVD͙^>Ysj~dgvԩ .k׮0 .>B!?*VH!@)I&ǟ={6+I-?&}Չd=SIv.|wqGIIɻDŅU.BpQ}ù \Ip߭bgy]_ F^'/\m]r$!(@p"Æ ={%QxRf7< yr7.p e„ Jߢww|.tƍ۷oϟ'iD)\4y_%\ 㬸4[ttc rss\"a 0tٳgwڕr ={6;;  Aԁ{i9IBBBt2|#!!6Zl^vM,03Ap3Cp03Ap3Cp03Ap3Cp03xxv}z /. t BŅأ7?psRuSpzk ں3I$70E}5     Fb˷> ##{޺eЩSEQG[whQ_5&ܕ]Kwu9mBݼTww:Zfڴiӳgv+WTUUㄈ u5h~pٻwozzXyyy#FС;˗/>|֭[B"Kll1N\E .O?tHHqWw:࿶JN븒_i@ʡCl6[ii۾}{ )!\&M$i\[<Ao.\v2dSRR,d\ElpYUmpυ}W\ɀe˖-43F} ȡ!hpG?9sN, #\J$ykVRCSüxL}I2rbQ:t^7Hĉ%oZ++[vEEE4$'\"el+%I%q|:JQj*U\\h|IW{ai͎T76|lfq:2ɡ0is&J6wrh.zK.Z(eEdfv`bODE#KрC604%aHX^!OHpIKKkhh72}H[V.dPǕαbK"s\A $ qiNJ]WM̶Ν;ipILL CCpЋ`?!~!̍_:҈Ҋ;Jeļҁi:o6q[GڗP\RSS%o|vG(6ol#w,6~=ۺcBbdfmS[GH-}9$#Xl% Yeq܎#k|YoOO[ ^'+f#V3ɅX%/ 5BIܠZ}g.Vϩk|`]LWLo&ү;n94Ӊe߿c$I.rRsV'Jr KK<+G(j0 9qBr!E(QFIQo|鿦sy_<\b?№<.]hklEaL'YG'NҸM;--w̿Ot’_y}ˏ?{L?q9 @/\aJ|h.zࢄ~X_k#ZEm2UdizKWn\y[:h7ZK@ @/\2x`B{eZK@ @/\rj+//%. =4@pQBj};zWUUEK=ޗ ^ (!E]G}|ER]Cp0f޽;  >444h R!!!Z4  /K`<.` .d7N %.` .\ @ؠk\\ Uqapa2qS 1ҽəApc0Tpa#,0 0 C*50 *.t Y ƀK`sr· ?qޠ 7.` ` 3Q:[ݬ(P?vg–ؗ-c_q).5c2nH!#łƠEťݯݴq7l/|%I%j[M?]ywda%?sӎ9Ѥ!ু7`\ah\Wy%{t($p4ȸ괟VS;??]:.vdfly+QEI)HoGios^=w G_"N{*.`oEpyJ_үn[Tb_QM(ĸwOIŧf壨5uKSSΒN1c#} 0+*%-}#Qb\݅DB;Ajƒܳ+bv/~6/h!-PQ[hqߠ$T=wijl*/ F` M-}c{:%W -oϸipX56,Yb߸ (+*)_ePq৊hBg3.8Z8gihnV7ù3nsNGX?E-l~.Ҧ37>EcOi18!Z:JKA #+*<エ`M{]k,c?li=!yǕ@p ,\F*. .Z4Snn iB! 1_Iw.. k.fB! 7A{`H չ /W ƽ/360\ SBR YG̏衃\!H4 vp)ݰmt`0:x$࣠?S^  }srvB4  2%Y}LιM#y[@=zeE@~ĉ 9byH5HpQK\0{pӗ9iF]ao͉e9)ۭ"(oi${&\;jӍM|;OI ͵C' Ə璬moYIyʡCI9וqFJY7¼Ss+Bk$f(53JFgW֗\]{qti'v8a,%:ť[$z`ܹ>d!*.+WTd ҉.W}8n-^_T."ɺ|}ѹeF$*sנKpviWK]ν[~m\v ;{[g#^-%I϶~չ}9e+K'9[`3c'&@k:.WV$UEz̨+iij!vrݗ_ZT-ћ:0xegdϒSs{)-c8WJx6-']qE{?qAYaVv]A`"wxx.ی؂s:.k֬Q&J_\t+)WvϒS{)_$1k,jh\P8#Et\֭[0lĈM .Ҋ #VpnEt$4(݃ .&%%EM/>%퇤I{'%.']/S­"tiAp\tM_^a< ^ Wq=i(yQCQI>(Eۓݞd4Is{r\gpXM6huzT C6jWfpҥ,@Cp\tMeƍ -jzi>`J P$h փc@p K\K5\$ ./.`.2%0Xq I Ye.`7oz[Deu<\F`H.`R8Kj0x)ʿ+FDD 1*&[ExSj{#I \$ UqBJ   I q0X* ZALr o td^0f5eP̃z8K LBl2:MnCXX8qj`) A)_r###\UJBɞmݺ&JMα s_ ʲ;8ڌWy ALB࢐Zqg^ .D5 Ʒ/_Z̭"gzhj@ LBZBŠ2~n$kvv1?Bx?$d9ݢIVㅵs6pn[KcSp#Zc$o`T >vc[E.`ׯ{[S=zޥcǮ\R!ɛ7oY \7dlk|0'^l=(D> ʶ!$ ΙBj*|*)CgZdllǺE [H*/]DWv0 +.WVH-DRR[EeZsZyV5g *$H9qUk@%;5p@ e͚5 HLLlRp&Lp|f(Hgd;ƺX\`pHf}og)h\$4Un:BA#FUD2ĕ$!Sqq!3|e8­"imF"(w ^d_\$4.ׯWH-d"<2>LDRKs5[iDxJJk3QI"ysqd o/ʗ-Z 1h|hƍ 9 00`5 5([q̗Q@bY ZVlP?)b" !g |w}UT\0{|"hN13B-Qq3`Ņ.䫙L f \N oaBѺ HmD F h @ :Rj,y  իZ j`Ha/1aj0rcpc,1oK.`U˨\F,0җ\0 Uqa!F |`,VZ!1 FYb_ b'0 CU\5K %0 _Χ*Z"yлӧtbhd.k.AzҲep"jkk=i\ Zطoɓ' X,Ν;'$$ <_ŁBp=!VVVV~&lW=JP6>|8O!!!}@p=!VFFƺadφ3]$_ܓ+\ȁX]vq!9ŋ3f̐=p 2hM2tN~'ߖ ./_޵k׺:w֟~_|Q%\@O 7^pi}Ž _'*.O~饗\hnC!-Zr:dbtqdyޓ#e,ѣGWXFǔen}Ȑ]>;O\< d3x .-E7[ !ſqa ߳煾Gy{<7FW~P2}t\jjjyt+y=wM49a?XM8-s+ŚRHpYj  GMi9'XuN)#ir>mO5s~Bf%J!L:f]p~.f0XLNSKo^bΑXNKuu;O$!`#m]{wǟj޸u܊ e„  zgwއGp!VFFƂӣmwr-hSqӗUiPnnlp!c˱c d\ Zgڵk|plB<>>^o.zBݻ#L ҥ,\y.'-[ z/ڵk8pY...........?q ܂;{pz7TmOb+IENDB`guidata-1.7.4/doc/images/screenshots/bool_selector.png0000666000000000000000000002405112604157370021565 0ustar rootrootPNG  IHDRA'IDATx XU(*z.Z e ӌןW{M-5+]5/ %v{_̲.PXٳ;;3;;\vqf=̙sf>s{YMee% x&Qê m۶gϞ~~~ZVv"SfsqqѣGVh=zxg4iRTTt݊ [ Nt>>>;~8*x%}G @%m۶ݲeU .ȽҥK]vjwq̥Wrf OH{,wPITTTtt|# C 8qmxW? ÇA]v20ECCCi/O RSՁ ,T4 5 s #VפiJ(;vt2$߯^}$"鼨[TkΝ;sU޽ԩS7n *D0kڴ)|aO>ͪn7r E49կs9n.tŇssG,}͂F}©p>s=py\<{j`Ccl>vXrr2.E)׮]{s΅X}|>}:@dj*|àٲeKZɎ9F5hr%H/ׯs&v͚xb;ͼ+ӷo3y85Oi٨& qrwO-G<q]FaÆ:b{gSӈ."^ɾs?d REnܸaA'UH8Itt;rq2eK&$_D,ڗCmltUX4n =^i![ B_''w4d'mUho[[rPm/"KݻwAp3n: #FpO?!hPF0{kHFW݊Yv0@\W_}[nB?IM_%ħM0$999#Go v]]ks#C}TQ,_D}T}wŅ])8o7sI͓μ<3ڼ C'lo`klşb+ߘ &|+:f .|衇`y׮]0^{s`뇛 o"\҅ʈŃݞDD{Jr ƀA܇s˯d1)Sd^#pbp8!4ho^lmzu0n[zT 9{<Ѣc'lۮ79uJݮqɖQI}TyCK_l=v}h'[p.[.>qI)/~4܃ϝY~STuL[Wrnڒg_ Nx/O͎MC'R߿mӝm~nyk J{111.]Zfo& =q _?~p*>|ͤyn}\PhW955[6ma> '| >nܸq9[ԏ?Cy/|})z?=[aiBf]OQ3a_2zH,z_B^|r١ee/pD?nznI=y`)EQ[~ٖqЏӒ)E\Vũk"mk.<%A0Uj׮ݰan޼ *?~U>:~^ V=Bͷ /<ݑ-+'-zt~OU/[Aܷo_ZRRJ_511ԩS0r;$|'L==%b_zoTw [A$}݂-|,ge/;w#nR~[ɨCX ƨy~ҙ)yt_Kn+d]F>~T{cϧ s Si8퓩[5aWt"mC//)kQ>XEo-laT~GDnݺ֭[i۶?ؤIZ'N&kr2]Y v?YСCTC BÄ~JOOtb+]zd3 1W{- D->:-5|]Px2L,bzN>.kbĢJ%"p*[F X#RtYpb] }2#’ܨ4 {#3\ũTCmm~G9UNk^ nǢM7%9yk>,ĊB^So)3+d"̖pUkٮ1~b@n266iTgIwRm ҿnv\WC@~^ZVDGGC@옵b =3+WMmPɩGo\@,W0'=>k5O9TrpwQJYӬ]xm?w*رCZH\ZZ>YJPyƷ[駟U?`0*WlqzJ+Wl#rҘ˽KCﰳܕm*{cȑva'qBi^M273!C={Ӿӯo+yVOu sSh`bf4 miѣ*grc(..ׯ̣xƍCPgKO?ެۧ Xs@ml d+-_VB3f]O?Tx>>>s驒k׮;v{3T2W11}LD>O3Ȑt)Z泌9!Uzرv?cw1h\RR*]F`]tA%GVʸq*r%ǏG5`U944tƌraaMBӤIPyӦMv!/aR*#jUFTTQ 2PeD%ʈJ@*#*UFTTQ S9=.,. WԷ:EMƘ#ׅߥ5>hި WT{Qa5=oN06#G`Grnky/Fxc=$YAX?#o[0$$8&gw;LBZu*gÖ0qګt8Us3ǒD81z:8ĥ,`[3(-Y9/l3;]IgI]#_zʠV3 xiQ8Jp88nV3c\i%ʼͶU%{N%{BtH^En 0T&m;x!ǶhXnVYld-2P mLeXbdEgfJ`8Q4ؒlQ@[ʙM]q^sEOW>f|͍δϙ"I2Oe-JeJQb,ӟHR pH*{$Q2PeD%ʈJ@*#*UFTTQ 2'\*1V 2PeFݨ|n̢;ֻ AdF"1c<ꕐk>&MTJ̔$WB =6 uryBc\t'j=\4bQyQa_|+B%xFPe**f"(OaU/%3S8D_ P.LiKqt.㞫,6XWYc|j: %`RzgpsO .rNT.v&-.ۦ}v>!^䊎ֶgԼe5ݫf SDĢtQdLT#^PeD%ʈJ@*#*UFTTQ 2PeD%ʈJ@*#*AfgΜYVnذA(U^j]Z &L@*QЬU*״vPyƍ#F樼%`Xkӝ[ F% 6lv8fsnnT46U~IK'2\AD aÆFeۀʦ8SّsTEjj:QTJ/Т(^ʵTNJJX :*R-`Bj,Y-0WFC4# h6GN TVCq`|[Sm $SNg'Eh`x#FiXc>Z+DOo")))Q9>OW:D*;*#*UFTTQ 2PeD%ʈJ@*WAZZM@ 4Y\Cn°}TUv TY9.*+T%Pe\S<o{M)7zOX\ʦ]SO[<|u`Dr@2NpKNI9G/K*޵L9;l}lp# *3.gFX tۉ96&æ CѤ獋7@@G;ҍy}vy=B*2gxDIJ,uљ*PwpzqUȽ*c!M)Hefܘ*Ofh',ۉ$KTofPYI+] A|B (XeH2ۉ`\ёZ`v*ڼH,ZQ*D6B1#qn_Dg:MD`g: 8*QE\bWөw1 ~pRqHvHr@]UVKUv TY9.D) TQ?2PeD%ʈJ@*#*UFTB~e\RGNT2A K c.Ll6#HQV~ЂPeD%ʈJ@*#*UFTTQ 2j3|"Wt4meXWgYӽjp!|d{:=((]  *UFTTQ 2PeD%ʈJ@*#*UFTTQ 2dVy̙i Qґ_ke NG aTc`ܸqQdA6e%RE*z\r\\ܭ@Pڃ*+T%Pe*Pe@rU <~89?P y;dn ND~% VYo'"Nۉr1pRH$RŴOd$m͌'~>jh݀|N$ u*2.9܊rUO$Uv TY9.*+T%Pe*PeTQ 2PeD%ʈJ@*#*!?|@@2. H]SPpY QHSPW1T&6(+RehAqUeQ2PeD%ʈJ@*#*A=*z{{݊Dxv\zTn9r$##fYVۼysЫWB(Ry;v0mq?)n={:(2+++n;h4{uqR9...8^͖9'$?!!ATeڵkXwgf~~ٳE;.@U*K wRɳ;*CW^R^^.wc݉իW̙#qjS5^Slʜ*p7*gggϛ7>aÆ'wRz=to%T^bE6mԧ2eϟ_U~wq/]c/wr*noVXXTzʉWn;)fP۷WS~lyaVgŋkQM꽃ZMv{4]$8T!;{~爭[GkW/kƿ3%T^hQxxx+[o]vW7Vh .,]>>!5S[zv|QB?rd^Hg^sf*/\0""vW0A 벫{Vuܹe˖W' m!u1sōM:i;0uZb4*>sV[,)u쫠5uWΪϬ3$z=i/rnr ;s;?7f]rCBsϙ9+`kkgWdK9HYС_֤,K&ft:ɴjժG5hA_%wV}frɞ)_ Ӫ' {Oyn;Ln#ٳ;vXXYw'>C;~3pSGjٳg׮]* wV$pyoHunUv 4Q\g* uvTzCoc[yܿHYfe޿}fL"?}v9i: gΜ *ղwk#ן_?"Dvַd-Ib2_f-^;TްaC}TySE~]{\o8xsty4s$cLfZ?7u*O>K.7npw{L3" "Fv8g5fN-3SԊf͚:u*11>~wlq'n$~K!fwҡ`}1z 'N*geeXw*oٲ>V{q'n*?ݻwp܍u'֭[qqqK~MB 1Q0/qaHGDDoT>wG}T"Y`AHHȘ[n/[LTeq0nYJ#G8p ;;[ek4-Z 8PzqQ޾v\zTF92PeD%ʈJ@*#*UFTTQ 2PeD%\@GeDx*q r7A;IW'*YIENDB`guidata-1.7.4/doc/images/screenshots/datasetgroup.png0000666000000000000000000011074612604157370021443 0ustar rootrootPNG  IHDR2IDATx |rمQ`VUHXA(*ŖC҂#hZ$ 4 ACnrٝݝ~/_3fggNJ|f9εqRݪ~çE9s .^h} ٳtǟ={6x;%IIIGJ#>hIEkVy'Nn$6VNiWufѸv}4?ku-DY~g}Ӷm[wTX{}ֱGZskDDEDJaF][+'Ӿð7)(233C O?񏫪DIJJھ7W։RBB:O9s&X`^"Gw]wk׮ٳiii۷?q%[lQc5wMqJڊoO~ѵϊURgq$K.G;sz_K]➝;v&2Ч~ϋ-0̡5:u|7WX!N0A,X@RZZڭ[7`RN?FSpʫ;yJ# @B+&MԣGlC'7~WdvwGO;O?CQzDTmV?p[o+)_@:tH'N%Jʙ7ow}\|SL)++3gZ]vyNF >׭O(7*~ss t.E}H$ݦM3cqN_];91IuOԶeבwyqqq"!M6o=+,|ؽi*-cۑ'O{{{/)i~וcz <zy7J6_Ƞ=# %n >|С)))VR۷HiSUU!-}m~=U풬!ێj?@߾}EuNRR/m/y睧|Æ k׮oSj~fcj犯ZϞ+{:sn_s!^{ރ}}c ?*9ͩrmč{sOL}Y$!`?Ͽx]dus bY_ś7_pǭR/HM{gYO|=Wꗢ g8&.;Xܙc<.R /ǵ${o$bͩS233E"B̘1K/ׯ_k+HҶm*_o)NGJzIߟn>4 @6lP2eJ6mEb,tt왤k]."̙UVM \o?趗=r-Au_'t=PyDxϯ•?/?~쵵rͥG'gҏ^vE^6hгz|P[:pT;^/eG~2uww!(+~WL(?m[]3۷9?Y#o]ݷ=a~3w?풣}в;֯_?\9su]gϞ'xb۶mKw^\\*n?~]Z,չڻ?J]j7J#!Ck?pqƉ\&7"UUU>}[n"TVVӟ E:Ѻ3!ۏ;w׈?~kʚk6gNMzr`+o}T4\R?|RPuw?_ѫKMvw/EkF*}`U._H}۞wx [/ru}}GV{^ꦷok=uz2w}!5??uNJCZ. @"o>zhRnݪ5 I?e?NSnמ8V~CZ!ZڿϏ> @/|W_ تءܫJYGӧ(?}ҥK].W6mςMһ*VʍW?y`s[wwWmtoWËs72hڕJ_G7]5 XX@L]Ċ&SWE/̩?7ٻnmN?oEJ1}kd8;RsEއ r=4?He6 ;v/ '|:t\pG}t9(l6P)3qrc*:9{҈WQRtܹo߾"]͛7תU+eP AHlz:>ly_{__Ƒ _q- >H1ew?_𑔚y^nbTaCkVG7~+I];EnkwPr5.wyHy' }AHԔ_j_Ą#G 8 ?%{ァ~ v_:vN/rե_Z~}z"(_{|b=}!)Κ.'O~yPZmV~NǾ>ܵ{z:)ב:HilBt}Ro#<-_$ kG.9VqXBoW3^\U$?Jw:mKI<꿫ڭK5m/n_p޴;ԑ8&1R ^r܅/]xkkk[ju̙h%#%{: f"wn&Z]DG ;vn @EEEmڴ馛tW:~_ug ?.Z܏ONj_w:XmT{i}{K;6l0_*,,j:}t^6:%63j;~/ƮE'=(N8jkkkaG!qGITy*.>^dvuksrr|h:y5\ӹsgoNOvwy'%%Ei$3:NJ.;\ʌ)>AT랠S(~!(MUyx_iD1b_ZjU>/R/~ieexVuIqquN9uVޢMF#G/?xyӡ*( mۊukܿ~ 9uTu҈[o^z)@ xP;]s9G?Ͼ$JG{"+$B1b9 s@ @!C1HAkn}DmE mV#jkc!`KM@u_.O^s(ZH_99:h hߵ"vҏ/ߴDJvzѡCq.-5=wqG? ^x_W:uDʉ'o.`JJ `KFh̘1qqqޑH;tЖ-[`KFhرދ:}zWDjX@ªU@p*ʚS⽗fuv蚹m㜽Ma %#[=kLjj>r @X]J!O d_F$;=( 4T!TH"~F5\swyeZ;k1k~-\ss[+WT! "Z0iJ:qTœF` H#VQS(nD5jT]]]FK7p(/wъ+W3кQm eFV6aԂ`[VةNs׸W^y @RPQ tw'uE4tQwAD!3o^xz#_wQ;qEvG[7[Z؂AA?@/j\p8w3_|z wmΩΠHd/5 f.Pw/wbw^䫬l[ov!6@7xQ҅/,ɜqLk75w/~!]Jkۈeoڂg3Əz~%w!h"p ދ{@"5@ ^DG}DD0`@"oNDKsN@&P>}o @&Pw~& < /p8@ C1b9 .DJ\\\w5 e㷋M1rZ.a]O& N)@ z2QpJZޭHxV5_Z29,-DtgY@IEwE) &I"'L e,0lNT(r}Y'Q3*w:˾8jI'rHnriU^UZIuA - aBy=@;B $hes|tim=Ip.Z/:*.@#"X;aiEF P}% lLO"@h1& @쥙,,{.N)h1LugE @Ob+ȴ(81QbYqDoF4#+R80#V<(ؘzK3qDH e0!Ζfcd!0}.wsDC`v @fs1`iVҙ(43e3bO)6~"z"Ưg/i8ug]xwy?AJg~(xqVڞĉ<XYYYAA 909}VI0G$w7OhJF{E95JrQ4i5{d /yj o#edR9 xDtT@O6\ýcPkY_$xߴhmשX8Y8\lT'kjEfe4u޽{Ԉ} ՗ KzaNGnyGDABB®],X`fL,)Z%Tܣc.J٫:/uL;&Xٽj OԆd!ƀ|`m煤; Ӎ]PǠAIp^ߌ3h/Dwښӳܽc9q Ғ"ҙ#Mmvlxӣc;)E>(T .?) Hl \ý pRw(7#[lP4-W;EҨow{T_Al`ləJ|}6S[wE?-}{@jwqsJKK-Zd!@"RʡA|iE6_lK'-eKCc!P߸fLYVxEk%W!i̔;\?*o(SӿNokThِeF'l"&4i$mQ?JRcvPrNClp??OD="-^8cO q Q0D| Kt> f͇flP/I/!:"IС0Q2Au3'Gr9}}0Vre"{Uĩ[^쒾K59jM,geXTy+/tM Xa06a;>,J;8.>;H;j_W_ۡrE]$J:vr.]j!Y)ѐ?"no1[o/4n8;<(J#mmDLſnΝEzg@QԀ-i?Q8k] z c:?O޵kg}v!:utݻM6nnJJJ-[F?~ `!Hݫ9sF ZGd֭=./(3aFݯ_&5,ؾ} @QF@+ԧĵlԨQzR{ ==lŊb+A~ O?G @#M>=-- wdIIɁ dP]w^teeDڴif(ڵ!C @&z/3/:5)III͸WUUU;v DC`vȺ٘K+2Qh1Jc}XZeƺ(}J"]z"s1fCBXlvϖR309СC }![ trM7Y @MOaE0233 N.6l(--lN ~ `~ ^\-ZԡCpF E8rɓV@6GXH4~ UWWo'=,&K։rj~'&&߿ʔ)O=@ 1@?7K.nDo޼y]8QQQCY&TPbϟ/26-vO=te7΋ⲻo߾iӦY&Eȕ5RZmøZđE٫W9tVC )kֶ[mT*wxѻ5nK b͛ @K>lW_)REػw̙3c Æ}!bBvQ2JH>$wU4.kVݮ/d߼oyΚ59A  *\{|V[1خ.aTnciVE={j#@bڟ?9G ]]v-ڱEr:ixhH 2LG3T~HC'xʢlb gxW&3lPs& 24k֬ mxꓺzzc!&ۑ/^G";Wi~ ^sak׿Uso.TT+wS=7^H.} ss-uh++e]}V.J&Z-t9GzK#/P2u# [ zkyr&n%5gK}M %fخ_"=Q`iU趣atch~I58AA Ќ3zҏk']}AkN;K:11RH*$O"q'E2Ԓ)wM٢g++({vavQ@S:{R+.ľ%.ʾN;RI.%OcNpiwuZT Xwv^H!0!9u RÉv5gYr N+HYo'h]ޯ px_lfW|-Ǘ^ :dN%@#S4c0'T JЌ}NrN{AAN @A XG2gnډ/F}5M! Mڨ'=kAw3E9B`&OOt@KKK-Zd5h[nǭ (p<;4,XU|VzC`@00EۍT7S^Zw)uwHy3e@$8 5Wio</۷'Zy6y:EkI5ȩ},|wW L78u98\Nf`&M$g{ėQRSSEZxP~Vfb젙btМfdvN$hؙw,l)7a|-}Nr~ ?L=<ԇЌSt99+yܫ%Nug ^FY>V^țצon;z- wȓ~w, #@8 Є D8|Zlk7}V6otǎ].ҥK"pRVՋ;ܐjY >-tZsl׼m@K3@ƍj ۓE=N폂uYgyn/i%7A7-57ABMh5VlXIpՋ6nOa20@cǎu:P˟ߖ[ ZdUwuVRRl2 |3"yG$@{U_vK Z[vm}NwEe ѣW^^^x6>5m)ɩm۶JV?]*[17(##c˗/L`!5W^JOO/++[b D.hiiiNgKJJ8PPP@bpo'OnڴIDz3]v:dȐU5 iޜZ`@uȑflCba`kC`gâlٛKx4Q{Ȋ=l/:͂!0+6Q 1[톳 '^1x1ĉ6mz뭷!w妛n0`msZt #///33sȐ!taÆRњ5=@MƏ,dɒ%ƍ (4WT @%`ЬY222O @ԊR^^o9@YYUf9F&9fU#E+?kZ]$Vp+:Dhx吊܅٫W9źbEoY۔b;#^sak֫ Z%d'D2+oѹ6:+*e]}xՔL_rsNG^^|eFrX'ԕY+HrJhjϖ|ʐҒR3ZE{JdXxPcmS@tZDO,ǍnyFbAЌ3zҏk']}AkN;K:112=@UE_5 @!ʳĺrV)(mVV[ƗFtWtɃdf?{ @:D[䫫^#e^9 xU}%i?hzkw!s O.D?ץPU& yE!ass>(Ͷ)-H&@@0:uj޽kjj?}v5hɷǭԻ vZ`ezr.d|dIX̞uE3f44ʰZhDPkvPP'?Ӎ *X윣;u Jcw/skʒ}+9rGf_Z_TﳬfP;b!9Ǡ:jMvը;'&pAkN :)|`&P{2dԔ!693tj/^"Z>֑Yܴ!_Խ[1"hiaf)sI>A+zg0`@ s9 4yLm$x3>&_ZZhѢ @}5x')Ѩ({6JG<%}&UZ駨U\ssf΂GњMPߘfL rEk iϔkXbMOL34w𓧦~ LXj>s1({6FIM.rzm|wW L78'r*q 4i$??%0hs%*ŋ-›3;G>vdgK.vV![;i\0 ^ő.l~goC%+A(Y__xkL= ?L_:֙#[O2yǽZT3xvIyȳf?r,j:,o7EYMqx) x\:ˑ'd@F uh+I&Ljˠ?llߤ;\Kt"5# 'm(5B>&mz}d`AbA"v> fni- !c:oK-YmcԻݺu+))Yle5&bDh޽jK;NvAk n)nzz@"ѣW^^^x6>5m)ɩm۶JV?]*[17(##c˗/g`nzĵlԨQz5VzzzYYي+V9 4n>}zZZL>-s.{?w6fòY=z4))j߾}8{xI[%fdSz@Vd+ Ou}XZ 1Ȋx3 [m{l" ,%qJ1 Y lq\i4#{GsZP<@wD:yMz}E"t妛n0`fsZt 5///33sȐ!}hÆ 5c kZhѢ:ӏ+ӑ#G&O,B9BB/\0--Z-7HdX@ôv˃Wѫl"H;_…\ujI}Uj^ƹK?I٥oa7xmeloߒeVDn"=Htqw=]Fn$w{uWom3On^m$flWྯ Y]'j,=ߦ_א ͘1W^O-vﴳ +xhhҤI"?~\-vK?aK|"55Uŋ?7$RƛsCLӬ W% .#;[r9|A)0Іf":&I/!:"IСQ(WoeFcgNr`DjSݷ%}FkrԚ>V^ț(608U`&L bÇe[Ak[I;vt\K.j E{!Vć-75o (j ۓE=N폂uY qe$h:K Uf5VlXIpՋ6ڮ_lm@ 3@cǎu:P˟ߖ[ ZdUwuVRRl2Pi2B}Y~ `Hݫa`R獵$ 1TѣGׯ\-~򻓕OU=~coPFFۗ/_N?BB q-5jT^=@^VVb ͉]tҜp98pLMM6HPQQQo0׵k!C 0LѣIII`UUUC-%睛]{g  `7Ȋo....ڻ@>"@@ \9WZj$%QBy@hq -ޯ;)eM @MVUU>iٯbNDFFF בZ`-f`EٛL{{ 9 1"gy&ڏ`dܸq1vg8qb@ߒ%K@>zD뮻B-eAV( 03b$hE\swo:fmC.ɕVʷF#Ƭr;*(WD:16f;JG!1tve+#I$w\!{{MM (L33 ZV\9S{˟=7.GY,ݪ5:>17Y$#M@̓ ffQAJ  5Шޒ @60z)ܤp:H;H+ywQO =@`c&2@ܢ mzXhkIiz̬O \[]_zءlwV833QjC]\Ly6$@L4(92d]83 4+P( 03P,OX;x?6F"g.CǤu1Kk1Yzw.pZ&ai- 5z @@6>ali'9@=\ ah">&#~Zt$7AE@D@-01 03 03 ~ iů(dE  03 03ƍji~4vB3(l8z,6F8E@@D0D:q L06fOٵ  03P, 1"ޟC( 03 zx DC`vBlb]{[  &A(u޳dlD=@z 03z1lĎ sI`f dϸ 03 03 03 vB33QvB3#!,6M@D"fF"fF"fF"fF"fFBXl5a~ŸfF"fF8H"0@@D@@DL׈bD ̌DLcdžZDff 7f8B33QvB3c z: Ql"(dE6~ lĎ9@g>@`fA3(0e˖Zjxx˖zk%CXA3F56Z(5}\\tխ;n!ya[VHjDOLDM 03M].MzSe֎jnôƥ;f[IS?l&Aq&=@@DLddA"@`fA3#A3#A3#Esb̌ĎĎq &(@fF"fF"fF"fF9y#i"Iб 03 1         "_˲0 fF"fFBXlܿEؘ@cƌ 4994D=@k&1=@#Ɵ̌Ď!0 "@`f @_c?<LD=@`f "@`f "@`f "@`f& @v-0)Ǣ 6fD 03}"=@]{S!003 캣̌9@g B!;z8HbD=@ +qP ff b̌㛠L:D33 1@`f&"N@`f& @vS`fF"fFBXlܿŧ@@D@@D@gA"{c lq@| ,~ î!D33Q0`' 03 `' 03 !6L"p{Ld :ޅ-[E" 03 03} ;!ĎĎĎĎĎD@slޒݿ~@ Z9Z@ H @gϞ"E\g/:kf`l'1-)~A~O\Vll-ads=zJ/=پOw$*=@~9O갹Q-Lx=OY䂆9ouXH~=T^$U9?#+FdB4ai]{d"KkFrre/ <29`e+ͦwG潟ծC}KwNz Ov>raDd%>/߃oMɛUW9*Zi2oPCVC݇OfuJFRR>S׉gFT]IqII~Fޮi\eN>3+)G(/=T Ϋe~ﺷR|YvrqϽRmdxz)孬vpM` lsv (陑y1#^Q:Fє[9C(VXQ;\߂~-=HHoſ'䀞xw/N9]\;N {xځĉ*0r,ʉǟo͑8 A% 77G#]gz݋UV9!s4ۡsڏ~_' ?S)YJ%k6Ay`w1@XِWY=z~?hvVQGoOۗ<&ngD3yxF/d;+hOϋt 5_}jjofnw PpgTNL*N)R,C`Ⅴ/Q0G E)Ҿd G`_nmI}~yUIЁB F2rXe4mf\l|Y]ٽOObh6t҇͊8Zh_H&X*Yus"M%>XͰ!jIQ  Hv?[/z6uai6Ќ%"IHɕ!ag+{EW/Pb6&k7_i}'5"޸˴s`QV,#Ӣx- C-UUI",^<l߾x衇.kvЗ6)hI+(4{)ǟ3I?RкDsJ5 @zpx_7rԭi?(BHnJ?JsW팫w>v'=N:Io߾iӦ@ߨw kǧaH*>F* 7Dr  s#)Mjy @ @KOOW5\V@W>&Jx'wwޙ3gp%m|+}}BbM&~GZ'y[fÖ5tzD! @j UgX)G]u{aqGTW'/JH?yr>@_nSA ,Ul|#v,Hj mp <5ƺ~< PЌ3z| O>sZZ'^>U(!a{PbbbYYYAAHs1P{0Q @ >>XX(5v_ء9pN05J:uj޽kjj$wOwR+T-dG$8conJI,:_Zr=F&;\KB5 @A &4n8;<(JvڕV0@+V5j:w,3RT!ȥZȝyfwv̙dٙ9@0C:3VQؘULD@%o A_l]0!@F "@@v@xC=zt$wv)Vqhܹl;I&KJJ ?~<:e@ŊcG3uʡzdHl͚5,T(qDYYSEsH˰V+5c7;Jlہ THG;V)^s<A5ɋ}Lj9>|`@Q@uLL*%{)ZŹښq>2z)(]J b !B,0l3yGC|F=H"鸾QK A[ݎXSȔBNj\FS Bސ*Eٗy5Y%9H9Jy:ŻOv$<4l~tzXpIΪOgVݿMFQΛ]Ua@TdbE4"@0FUq|) yCBe$[ ;("Jr rZ$1mJut^m,-E;#-\PGNzH"$ueDRcLZ_LhN3ƟJ,E#}tBBi\Y5CH0*CI( ^hWRW ]fvgUA6F=Є+sZ Z2!hkƷ5In-vC*R;Ub|E1 ТEtzd W4۫apA)4bPcɫΦToz#_H;尗؁}>r:jhO~w-P|VQ 0ǨWv<P]K)Fa,_G[3_gB? %cO.|-fVTHbؒ%Ktzd,Ri|fJ#7!%)L(<=欒79.cH#Do\(Ba>qy,)Cd[ՠD5tPl*O#@DNFgE @ &nwDೋPYڈ[ <̀Hs xk*=x fb,  wD]X A  ϟOMM5f!{ =XQX+۷oߺuÇQ:u4tЁ2b{ GC@;LPx ShrR͘1O>C eأw{gEWVhւ^6y )0nhrMykb;;˗/Oeiq u6ʤbwsY:4m4[i @ [nG~dl}(nqr`t…9[j*|AM>G:J(]~dSޣCwCYϜ9p*njv  #}k0cG?z|)>\hieq0 Pԋ]5^McsN;NΥt\Unɱt4++} LIҩC-_|PS$Cʾ?Q0[`{u\q|eLiJ2$W<)TĊ`GB 99$)/8tW_;ܴEF̘Aˡv4Vz5w=O2rތ.SENK7ء}Wtb.Pt @X8#@yKԓ^RN@%J~?,8]>  ~'|wW\ >:PL9}n_Mw-ْΝkyD73 _ţ9;S)Ir(:Şګ3!W+rz"@LG3r m(,$#0$pZs-R'uI$Rh #ٞa$&@dNëRnӟ]vU՗hX;Cw kxC+SNu;e%(=MZrtCs $~ƴ+f :)*'R*YQU{ /æ-4DC _>XF6:XMT)2ڝQ" О={|)cǎUfXz5ӧMԊZ>)? PS`2;j|^l)+8 42e (6ǧ{۴oZ=x]o&n v$:,X dM%2b!rH@]"@B*@BG}0ڽ{2"o߾6 iPEՑO!dB+ *@>(3g(_iítPzKsخ2sff&_~ #jjKS5 wjLM9# D$@V6H)05c ̓MxڵKN)rH5(l xC"G=S~\ t"@?0'OTym?ܔeA4%#7Wߛ͑ f+i۷[d֊;JH /kڋ(+^b TAG/R-KfIWltg\<0`ˠC P0V @&2j\F36+*"@:j_Mnj&^pm_3G}/cǎT.]BU /@S` @8qbnniJҕ=w#G󡯜o[uu[t]o9__[[믛(@Pܿ]4޿$6_y~EИ P @#@P:tMS;|1MO^:׮[=v^YNStBVw."@*@.Ϋ:*'3>k7 @aN47}?{MZ;~oe׿@w{A  N3x`Ϳ"99y˖-v ko@PD䢢,.]ݻwժU/d"O.//o߾}؜'Nmhho@ t4sδǏ䵵Ǐw\P9X 3T" X*@۷oUȑ#a2d!@@xjTflW8ҦC@4 @ l  @`;kVm{UF-@ ۀW¶^"@m `Iӗ|I+H-ƦR~pH$/H lիf_B_-:uʡh<"ȷmcTv̾`wCB… 2=oHOkbvO+g^<ӧí̾p:^9i?˧:O:qbmڴ֭/ vܹQ [Cw$^hڵ4٥_hA6k5BLٳv%кukj?T}z-F(o :@-Oq@Z4gK9v~Eo ܹVn}bd[DJ'NJ\H j yE8 p›o9hРm۶&%%~NN84h_'L μ)4U~LFm @ F0} @%MZ狏28kGmt,%@3{`ݕu,rT(}6=׳Sɩ9p YòC~uӛyBZǏW%Zt.*++~6`xVZYJ:;KI>5S4z+b+@uz̺5kv8&uLO?}&dzCρpXa ox9f7|ջ5:7.CTΔerTgݕtgF= %L f҄iT*+ ȑM͉ʨhA?G9m# WlRSQSvq=V rƪ"ʳ=bꪢVnڴ i 'e@E/@k>˙ag323eFJy~* Hhdݕ<9BN4j(U?8]s vwـ$\.nS(;)-aҖd]~(wي|GOP 3٤Zΐ_!`@R"@ @X؟ZZ K-8ӟ=9{:Gݸ373wd6->C&@wq075Dx}|vCīv-;Sщo23ڶNlB_j:ssϜnk7L}-q5DlK%Z4*g׮]Ln%.Ɖ'⍣XYZзvWa>L~b[ #b+jEZB-hyuk˭q"@bApxDFEЉٗnWlK 7fD/, "@1(ZߘBBa+T$ ApзF>tQ(p(b5@\U$ j5.H | ٥?= p+a[nӒ1V;gւ/BvdB[`%@fFhVND '@khVyQ᭭na@($t7 a@@ d[3W($>l,-⍉ !ă'MGݠf{Fksζ)xr*xCֲlpfӍ<:IOJJ1k P4'n}rtg]jV9ack>f񑣄Y䋙 >, m)0 il IF"@[5SKn_:S'"@:]m7ZS,2ÛBiPMd E&_ K|HQϊgvluڧN#S:[U }x0&'uP?g* | |ް_Mk"N"eO_>c>$9E>&/.I'^)^i(ߨ@ߓc>ԙa'#k b9b { D^AF8^̩"'ŁAo1G0ERENU/Iodpu3>shYm۶qmI|UKkղP $yόZyszeZpB)((Ўm"E{=cǒʾ^y"$ =skYX)œu[>SͽqcDYEՕpFu= HA-N~I%=*6WmR%,I\O>qd-ugpV)rW9ٞ-4p͍m(dQl$@-Z(N3"w#)XՃrUrm!% #J4Y7 S;oE$**ȍĈ;sf)uYì+y m]c}Wk^g_0H$턡nOMM)@QHz5##CZdB>|x3Р ՞9$x+"%WQ tSQLCIXsCb$EΟ?*ce˖ -th6lXnVQsԇ{xWMSY - o*,,+!=zX)4E*[e)0mf_0P$efOsx%S`V?`O PԈ+a(^G  ` @@(`R: l_|r ޣK"=rX0  ~$!kvǡmVDfZZȞo@B"އ ak(>_k0f-БYعn#l_kmn @@ *W `E`Mk Y D@ #Y¢   ЁХKBenoyѣGGrk׮hnaaEB)04-hܹld/)))lc x&` u;a~gبQH/VSԹy/|wvGV֔:x1v=&0bW]t2vX yOŢ8$ G0f+;͛c?Gc g!D)0`0Hw-CȶNU%:W_%f%7R=&@qe&97Hٜw#Iz/J oQ; o/^*/&3f… uRPPfbRA7\sT[9jd F6ⳙd7R:A.GM=֓-YYTQA?wpj[mΓ.(SGxpÊa% ,xPS`˖-ӱZ5 =F]\3HY4"@,#Ej5:S`XQS`Y@ī#v@ `;@1 u yOqq<Q#fа܄8Tdybձ"gR](} ؖ=w-[ {"$XyyY"KQ#3>5LE Z* kF4*q#@|%X' f=a#5 4vk!"h5@&@a[B"h &+"@@& ,\aEw(j8#GX X4\Z#]hvq8l" rT9]cS`! v(*6#@ `i @!s! Fg^Eȶ6_ C ,Ї& M?8{ߒ: H'D iJH  hP3Z`ŁNcQ/FS`[nGD,&@LZ@/ud a`4 ‚" "@:pXm D }xHa_aR  (8KIƷ05ƕp#+o尔878HaKCNzd%~j(%`T$LF ƭSK 30, "@V(0 6pqNz@Qn"g'8l1—]°jժ^zAH. aHMM!@"@w6(D0O4ӧ. , /0 @!DK#ޗAv@`; @  o?W̾MG:rw Ͼq$@8 Ev@`; @ liժUjjW_/\o[n=|pؿc-:u4tЁ~7qM @f̘ѧOf_W߳gAMb7>dȐW}FҢE͛7q v~'O^60ݾ|LFǷ,^XS/X]v̾X#>wԩ)Sh޸ ;BI&`nqٗc$S6+Do|޼y;wnjj2b$%%رcO<捫#^) զxOG^|E::rSO=alZlٲx/H.~U~~#@gϾÇO:h<d]wٗc$ƕg]Gf͚եKСCӦM0X[Sir/Hh穇|#@3f޽;_s/&59ў܁AmS;tJawlQcGCJEHUq*gŹ v#\GOޣG|mnCY>Q3gBm<馴7~cO=p۷O{soۇo8/#]>KҬ&]7y#ۻَXD{{\.M<Ϝl߰}~~HHSd+b/D5<{j))ߣK rJ! 0X8]zWozS׿p}dGOg6aep&ROrC'|wW\n2z=>'{G 8X-'˭y$''744̝;h<7-{ny;Atۓ;9l[AoT~_l#@SLӧO4k4Λ>zԶA_6v2E_hh9bٳ`MHҝhO~=q Pݱnu't'ZE _*k = J̙3Q݂^/{iay[K1o$mϽB|ggP233 @sL[jo_޻_˶wKt&=N_YRږ}mϞt:YqoO!SG~᜜'O~w"e Lf͟OݬIe)ΒC}uuuK,0x_jݯ}9Frev>:R;X#ر#KBm<unٗc$7\to5]uhĉǏ7b믯}!@66޶}9FK>i([G&L@СCf_t҅ Њ+ @6 gw4r$̯~ZC_*..[oI=vUQQɓV{][FB%$2s9__x/**/wUVi޸ ;BΝ;:_aϺǏ\.Mb7keOmmmW`G}{ȑ#St.iѢu]7dȐj =o\iժUjjW_/\v@`; @ lvh P!`i#@b @f_ @ٗP?n4s=IENDB`guidata-1.7.4/doc/images/screenshots/all_items.png0000666000000000000000000007466612604157370020724 0ustar rootrootPNG  IHDRמ-y}IDATx |ݿg7nJ "boU"WHX)(T߿ */ "+[+DV_TI`` r 3;s暽f}>|333Ϝ9sx^fq&iO>|ǽ{ZOAqĉO?t޽_x7xc߾}Ϝ9IIIǏ?s*ov=¢w믿N⯿c666{DCnݺu˖-܉'lQ$NffuxO:u޽'ɓ':tE;!o{{;=& i8l/gZ<V ;׵aἯ|׏f AvZA^?ЅٳgnZOVwwtt(RHs$VWW1{HfIj#Z;,|6p/{bvX;:pO׏ZdggKSi98p`RR9DwޱcGCCC'$#=ƌ{chw@sZ# Y[dX,Q96Bi 4gΜ!:%&_6Co޽{+ҿxںM;e=4ϽK֍|Y>@lٲs=~Cۗ﫿,BGM}taz)r mA(r) *(( ?۽}vr"M{gl:\mc 9鿶z}[ֶ_'=6c,c-;F!mҸ3w36qr o fM~3ep*u#G\uU!܅(>﨩+Ӑ&ȾJe}c {GLo^>ydt}Iw~[pKgJ'C'N|s (Z3iCM9-ZcS^x /j9OLI%\\s#Flݺ$)={#m\*cG-[l~%i@Rkf<~ԩxIH8?~ 7h*W_MKKs8;jgykZok-H7VZÿNd;j&N()"07|s/f_*^n;D|o濾j_)a_ItNTO~/#{S,V+|?˭EEEׯ_]'N\{ = }9v{GM8o8{`KJk^{s$QM毳[Zl?4;+{@&55K.a)MMM}yd; rN'AZ$Wh,VxU/~ I/rWta|_JGKK^zz"Ӷ;D(^:NHɓ%1~_B`N:t̙DǏ,}%_bx-3 , ( i@`Zx0-P<(L i=™܊R[KJGyCCj73؀ N(Z)o;tYBW|VV6e YeԚ 4Ň&`R4!x<_ʣ`ıVj aJhk "V<JZ,[+>IUS+J#p DYƗCU<~S| n$\@ ל[P׊/]; 3N{;.]Oꕌ'~(iw/p`nQtjv0w՗HV)]O;j4KOW*uI#E9\mY"ʕU._4BɟYrkPW-N7Jnt &`VF#D`2{iM =*3xIX};q!/:}s6ҜbI; 5B)[ `^e]YY[yBRI*K'5M!,L %q0 vc9WqT|hR4R$bBhZDT ]+o0^KrmHg C+#3H5Z?UU] ˟W yTEf݅GN)_Z q\[R9"x %cV'Tgu(-);j=K5zV~X풜{4Ez]v[>|գJhA`S2o_Z|)l*4H%XTxE ~dxe;{o?/D" (^bķ_(B}ݣK:Sq;$OƢQנU֕+?5^PjSݙO;)H]-]ɠF0AD" $/DoKQV 륪IMk_}eץplG\nkN|a u*WV]+_(X@8!]_MZnuw~mʬm7_*gA?iU/FAwjjܞz+?unk1䫎].բtθ(*vkGxkNo U#u.gq *E^u[򸣕Me3V|tk:s4]L"tըޠKo+2X=BExЭ79{كx[56G({> @`Zx0-P<(L 蒖񰭭---yx.D۶mܷo_GGGѬVkFF=zǍ3C]dNg^^^o,˦MjkkIn7{ݎ &(RdKJJ.]:`"0㓃đ#GfϞ]^^OVkЍ Tp9s}Y(?Sıi766>#P<or~&@O>a\qC ݻq۷GxŃD@O-)(^@DChҾQtV?ft*K:=u;1 )U 8)A#&BU^e-Aay7MVb⧳>s'Y'{ʦlZ n~Rk׮EPDTRZ-.ZԊ>.|җ:s~  FR(şY/Ja'2K1C1BGQSϯ+H"kmƮ6tʵ3CѼŅiE1)9֦4 Νki現mg}n%W-[ّ~Ӵ$dzx)^x_]l u2v.OwxbdUʱ_uG(e< RZkcJ8-)L;)WWAd(tԀD#(şjwf=Б妿|rw\ꨑL'tuJ犧't(XTT1OcDi8?;fx"-Pظɕ GhaZ 0Pٳľ7FLqΏ~~7]F6wS‘#G>4j.]4fw2W|,:jŕv CG YFK:&duhZ5ڊBGD)Ow+i͛bYZP9I؃'Ur@ 0PY⛛i\ENDO7<ڸ;?q]s549==(~1iգVfdRݖV6AsӤV>t+@HOPhpeXK ƮWnx@ 0P3>LN0n7pCVVM8p`MMʕ+Mn5 @Rb a}?ler5p@ 0PӉ:D[ZZHc> uY\r iєAůZʄ c ?m4$]Y%'~?yA^zˋ~' 2z͚5P<):u*Q|CCCEdeeů]1@w}QC ,s_|LJF~wuWNNNDZvz饗  }233ܑW]]Դh"( m۶l(>2eǏ=z4q&---%%%a[[[[ZZ@`Zx0-P<(L @tM`ZO '՚p8"Yә7~p`X6mT[[Kbvk5dأK0: qȑٳgwאec2%KdffYDrr̙ Cbz8VъwYNS9g2x>wv=i766>#P<)' P,3.<۾}{/R"߷oߣ> Cb'x"++KxSR/(*]U&;s]3겂;U aS^b,7V1;Α#G*!yEOuE߿/" {n- ]:l+ j]6}73|8ET+-EYfBLj5#R 5D(7f9e @V__ 3xDzj_pat{oxE|\OiCCŋ{ۂLU: ] )d&BR|$@0K m=bۓ˹ݾ=`[yuL a#R/_-AP9 * lfi j PPv2e;O/ |%*~bON><{L"b"gNJfrHP,gդW_$hf QE33ÚiHHbF_d8?7o^NNɕti?yLq=699y׮]-pkb*m.m+oSBW.5WF#6=lGͥu/Sx!Q?4^܅}i/s>>7}1jb)RdzաцWvfH5QgE1)9&3J,qTl#3X$ (~ܹ6;u .{\كMIJJx</%a(g}xQAmйHj5W(Ƕ5$^)7HQk^_&񵕋*3sDv VO5<|m6%FQ))s(>;jSF,*1ַ;ūZ(\cH≈@Uc^zWDw[0>DF[Mlvxu@,~ ?{켼W_*K8p`MMʕ+P^`w,z!%VַV&*7]׸ O>(СC(vA~  Djժ.wbMfۛQwXN v( R]]f(O:(;e+_^= w]w(Z!@Zvz饗  }233P]]Դh"( m۶lبx5,Ǐ?zh(LZZZJJJq@`Zx0-P<(L 肛&G*++OV5##p'?DN3//o^7heӦM$fV<^C=z![tÌOG={vw ^& 1/^dIfff[[[E$''8p`Μ9x0)ꩧcxw)KT.=#{_Nq9ǞDH+Gx@L1PO>9l0W}fqUW{zQ}}Q(?YYY ָrgr<}=ʏw744<]V7QP*ɭ,F겂g5yo&'SZ`W GSFtC5gӜ5sjkwV%|c t:G(~=qm:~:tlۓݻI+*,ty谭2$!wH0'H)t`=w5 Zʯ(T #aP?Fq!잚Dy0DVA搗1X`ϟ/OAg>6A6UդW_Tzm#2jdDs8M-žoef_G(+ay(n\IǬ3cwڵhѢ*U޸&v8 I:ոx(tR{e8bv\Aƾx9L B2uH%'Iy]a'"2K1Ck߾.#WuUʔSHh(B=i+'=ͣT 9H{_~ '+B~_6FSd?i^tZuVZ9{EKy`]| \):1Uq lt<4zSD&X?o8 ҿ}ؔ)^Ft$xyڱ-P:j1T{y9}'QեOwWW^[mQ&<թJvˍj]ti<.:ܒN_#98k)q;t/ YQé;j/w֬|8tp ;iuܸ&&Gy?Ӂ ̻I"䔧˻Sr|EBůN(8(2\TToO\RCFQ5v.g͚E̎ZиgcD˗/M+RJI˭*/}K뎛N/37;k yI^nP1aňEE\]j? 7bY@=6qbMϼ$9G1J;t1T3B ri(^VwL*zU $2*~'..)^0q8Fn32,K ~=]#~8@] Ϙ1#??s#ORXSSrJ(^VǕ`/.yeEǫ+-h4B" ӭ]O>(СC(vA~  Djժ.wbMfۛQwXN v( R]]f(O:(;e+_^= w]w(Z!@Zvz饗  }233\4njjZhqHv۶m[lillT<eǏ=z4q&---%%%8 Z[[[ZZ@`Zx0-P<(L @tM`ZO '՚p8"Yә7~xf4ŲiӦZ[+!א-]tDa'#G̞= / /Y$33-"80gL?SıVr䔥w*ʑ=/pڸꃜcO"#@P< ('6lB≯̪vn+H=(~߾}>(bx≬,KFkO3K۾ǻ.xwIV(o(uVTB  ևP Ynbw:#GTQC?86ؿ?^:EVAb$Zq~,k[ FW%Y6An"rG?fxJˆ+F^ZlN ڊv.(+\E>-)(^@EBCh\QtV?ft*K=u;1 )U 8)A#&BU^e.D ?|,,7o^NNɕti?yLq=699y׮]-%q>S81A.x؎`Ct/ӂx!}i/s>#y5Jr)FR=ohƫ\;3{T8VB*am2u6-,;wfkoogGN?x`7W`S<ŋcri n=O <ユ_I \ $Țlcۿjhȥ^c EվVΒzq[.S~wSTa)`- P~0\(U0s .7_ۇMy|"'BvHm2UN3͸P{;QNbX [y*\cH≈Tc^z_/O'W2kY_G$~|0Pٳ}><թJvˍj]tit_X⮤i5ɭ~m~NQ?!:N05t\Κ5Z{9nzVfG#XQ0rJZXָ!lz.~u֣V9vLvS'Ъg͚E̎ZиgcD˗/]YN邩lB[}3Gxrkp\undN{*ǟu$ܪ߉uφ~ 3EE\]jGRtڡTLZP]r79ktd|S3/wQ ;&^B'Xůԑcޥ#'>!=ݪʰxQn  ?cƌÇ=|ӏ >W_*K8p`MMʕ+P^`PQ\P@7J!%VַV&*Ws 'ZLB@ӧO'?t;(]`BAůZ ;j1@ӦMMMM;s'^lpk;}C ^f bNJzÝIگwjzZjۄOnkn9sęgzzI~1++(~ڵP<)GU__f;w|x|k>w]w(Z!@Zvz饗  }233P]]Դh"( m۶lبx5,Ǐ?zh(LZZZJJJq@`Zx0-P<(L ̊ӭs~Vẘp@%xixixi=B7h+,)3<:1&@]ŗd*D8\(`b "R6e Y\(Wp3gr>7%s?$P<DWnxhkb+\Tb ENl,g@01JMOE*^TNSxQC4 JB3 KܕF?)e]m,x*UTJmλe|ELLy٨P<$F<{_NĀ&&mxxixia?֊ni@`Z̬x4 07I<01x ((((((((((((((((((((((((((((((((((((((((((((((((*>++i+,u2GWLnE"01ol("DlO ]>6%Y%uiϏr;*Wp3e9P<RD'4;D҆Rx@+"D /5)rif BV{kP<DwԔp.A{o&lr*5|׍_[ \0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-P<0-PxDnx@`Zx0-P<(L ̊M .tz[UBѭo} s6DaGxkx3x(( (1# gr+*Km^l((Č*0+#rW\+>&(Ğ(޿9Q޹䊯(WvgU{TȈ ]jTłIE+x+@8) VJ+e9(-?bOLp\n>ٷw.[1{ȉ1!.56WV |*$kaZZ\jbsl~v.q'vO&Eg7S^G ibO$V.F LFLoTw(v.rT9V_]/Gxj`>V(Ě.s-6(SC $Fqxk\'P<b bŃXŃ d ^ ѭ(L iY 4 bFih(]V}B]`ݛ7oz,K]]ݸqF ŃpxeիW;3_oݺA8DAtĦ\z۷oŃpxeK GS!`?BV;󤥥'M[E+[|U[Q6(SϡPby_||nU"^/!/حJ.hVQ%O|PAÀ2Lx֊ni@`Zx0-P<?k֬@X|y#*~t#Orr22e q!2;I$#~,! X(ɓ'+/Xp ~QS=!"\/Y_g_Q&\Ż\./~Ij ߠd :r].7)]֓˹ݲ$+B.B6de[as74ipe"3opqB\:v xLK_l &M2쨡ޑ#L)LdsT7?,tRc[#zfM UOeP?Į2ƕA RҊVt|@~Pw]Dp3pwQZyU)$'ӏjvhE+ me3:Q•\$,kTA7^4bT+Wp뭷)6xf(cx_ A9ܾ\9ߪP.zsLE_z--I_fQG&@5ʙa :j {\%3ujfl!ڡˤ^~J`\*+VcsE Nh+^+йrUɯ#]9\ͨV=3PrMAL<{gS4{Z_c#.j )ֿ3`.Uu i*>@`^;jxy&@`Zx0-P<(L i@`Zx0-&Wޢb~/^W`4;e(`J@<;I$ ś凌K7*1; ]F{ibT~'L<%̇B >P<]+~ɒ%~'d\P<])i ԓ˹NIuŻ\.; AaVul8ÊkR5BOjQ}E~Q|r-nKDOW~LZ%RsQ(~ٲe~'L4ɰzJ]o]@ir1s-Ii]o"9%^'6P^pi!?Vi1Zow%r73ՙnQXIU3+El U7Μ@ /VԷsq ruc_eXC˭71=aßM=ۨKuT|B6/ T̯@ASbr@wi@`Zx0-P<(L iIt6nY&LP]RRt}&d=rٳˡx a+:D%{.O5,Y$33-3gγ>  [٠Ё?Sb +˗󾻔u.QSɖ@Vq0PO>9l0q.*|U9zgåzʲjy{x;F}=]H`HYٶmvWy>|dUu٢jiZ[nzBFA''V{E?2iw;BH )C+>VhV>k~_PC]_8yRP#:`e=™܊.%U(>q0P9r${|Jωۇz{i=ѯm;ι@bٽ{7ֵbfenj#Q(??s$8p MŻKJ°\1m(Dϟ??;;[q2|go-y˷J]dwݪ\__pWB\8GY8ްVv׵V]TLܓPljꊉ'm۶mcǎ|0?3|꿈ɿv7sVh)ػЕ+wI WP<*S[>߹xX> o\מҀ%cb4+۹O>?SRRYFx^KCMgy$o<]I&h'~?k Jw\ O ?o޼جGz>7>HC[F~v ~oy;经m׮]-Jpsiųjf~*e|;5ջO?WbWɗg럈f?Vh)0DukWδJ=bsSDO ?w\NN jB}dۣ7g̙x/^܅*R{1YުS颧YH+g֟\JY#X٢BrsZ-I nfz(>qYӽNL!n;;DWP[".[%ѷ'q~FZuub{.-ӕ_UܢP<={v^^曡MαVO:zA/>R-['l +(>q0PY^ܬK{oO{:zͬ+7ӟd[|/Ὼ]nzw8?~]$4?hv\8S~i0`ś&kV`.B pBPk7R')Ǿ1|WT|0& l~ӢY٪o&1?=/w1{.>5oz@3f?|Nꍶo&;uQ)-gjOMG蚚+Wv!ŇQE}嗷v@/|ԩDGA'>}::D߇7S6ֽ=G24hVZ} h4+4i?>wuQV"I}QlP|@񉃁Mfۛhb;ugO $mA,b^C !k֬Ipshv---- O@3gΐ={yB{" (>q0PԩS^@rxb@A?(x}n0aV֮]G8qB]޽{/`eCO w5>"w/B@MV6(>taB~wuWNNذ ҰڵkK/5 [٠x`ZH~233ɹsDs禦EA@AV6(Rm۶e˖Fͧ[b os[2W[+E‹[]VpjAd^X:*M "iiiP x_=PKړW(^ϭT|xiS<}T{PWUUկ'1{(^D8/fv(> Kܕ.M*:w(Quh F0_]V ?6Q(>!;w*ҧNvZ6eԨQL5 D<*b.Vr:2dXq^rtE/*c]ne']n8#e׼u (M]eXV}5/.k)^MwQxؚbUܚ8$HGMt>@X{ܸqLJJڼy3(>@رc֭8p`9{ݎO._@À5P<P<1 @ f@( (XŃG4mM  }2 幹^ x?|<(>@`cǎ=:_oݺ/0 ]Ԋg[tbПt ;;;3dא ]Ԋ>|EIֶo> I,4jAG=s;*4}444@U|KKK9ҺxS5!IMgeeq~ŋ't5s+ Bw@W3㿊p*h(>Àb&Ta8F[k{Tx"2{ݝڹSxP;vӜAOŗd1 iKs.rٴ$V㡊O|õtZzڿ,i˓E`] {DPKbݙJMODO%+9U)G Ĝ**S3&:"=(-v/ ϶o6no=Yёz3-NVxk 9vbXc eQԊJ[UX|)Ou@*{ O߮U<9<)'Of3tu9S)zWuߨD(ǷՒ:&P|pIrKz۱#OX<>T{jK~|OdQOaDUw; U|UU_O 9;];@q&/,qW*h4w덲߁DQ3"h!nG@/rĿZ=imI}zoX\LWˊ_gfU5 Q#dn>H2*{șS\r"!U{HYҀB` M,4!*́Ȯ*U CT*eVИew.Ft_Hw?3=OOj:k.$^7&wNeܻikr_B,YN 9I ʥbP2oPrSv(ch.I iHЛ|$-ǧG+r(hExA 4y݅Jzi;[/4M6K'y$2+{ܿLEUKwap>P>4WK,F,,YeGxE'%kvm>UGhEra5vEX$g>@};P\=hv_+鉕{1dWaO~2n As&Ln-oi%Sdǁ";YPJ«4mog@"xd=UP[HT@4P<hx (@@P<0xDa@€  (@4P<hx (@@P<0$+~ʕr6vm0+~t9fQ.,^8]ص eucqHvmc6p(#>`ܞnҔ6-- >bgIb#5gNʚ+.^ P<|oٲ#<ݰہ{L#cI\qW|\]C@J<ߟm;2ZNrA'*8s(Cm >/;U7voj;pv5~O~M _/GV7XpaT{B:j ܾ7PhIHy&MVn`VpΠf윁;U3,R {nnlSDsQ4{5o}i2Sw躪k >{zdu2Eص &$FVr-d OfaN ?sI4';ߤ[soit?kE1w2<% "_2]ص &$d .~vfgضL PWhBx}6{eܚH;~gzX.0T[ŽOe+;gټؾN+H@4!QcWQ2O͢ZSB@7Pj-ĪߋD_C@3PRy;sZ~RPaW<@6YtUzoYp(|(CUu0.:*jr 3P!Y(w'x΋NalVJ _\eU _L*'pu!iR)Weh@@_J*#۵]mx 4 hx (@@P<0xDa@€M~M (^4@4R|1F{j^Q@4QcQKُKQ<WhBxι< J2MH5Vn ?Oq-M +Vk56;t{ܼ6dq{rHgw&JŁ ƨkMH9[.#\[r7og'vӝ#@p<7i:B":Zi _6kۄDsoȕ3٣k)z7RJ ,oWmc6p(s*%B<,j>cWSMfnwVׄ(DŽkV5I)wQ昨-0(&[xftqU~57}37Q-M ޢ+:sx"A:N mnu5;:[F⹚YU"՚4;v5{K+Y߄>U&vq klϡ|Ǯf>(|s63(ZS3vUl/:S%xfTS:biU]ߥ(\8r ٬|I?])lфݝι&@@mk !_2]ص H€  (@4P<hx (@@P<0xDa@€M_'zRK_jTnP'zSƯx[ 81YOsJ|)do5rWѰ L7_-P<D(" @d2555ղSQ^.]rw!6pP<Qmmm bǏO$gxAxׯ_?mڴ@vZgߞ:uisνrIEE ٻiWX^ k&d}+T-[&N888(;ARUUg=we^USǞ|C/{{{z)(ݡ۵+F5~^|M6M<9zYv-Pٕ׍fò$o.6?sSL⻻}Y(ݡkTAmՍKd''HozQsb㝋Ⱦ{駟A5ݎ_īo_9fmvIDATx^흉sǝǵpVklY2Xذ&pQ#nˆC8@Zbp‚a96@ B0)56:@qx~{zx=ݿo{y5R.R"|A/?ϟ_VV E{@#AX\Zn͛@ޛI" ,#Ġ ]*rHnSPp(YJJJ(8 :P4H1c 1B$0*#G h.TB048tqꫯ 5 "q~+J:V̹ywb|FBt"N$*XGoEEE =Ђ.}T6L^wuQ~i=‹e3q]: $!-a22ޮ̞={'Og(/_t7|{ES#EI"-,,,@ Ҩ^p*z _43}QG . -a2g,ƍ۰aÕ+W_PԄJlҢSDbV\!W^mKY[?\5L~ʅ/hA;z!H0M9ƍ@r.o?kjkk'gΜ\I"B9HP|3Q*wœ;wۨbВ i]>dLchN 7BZN`$ʊ+@E]]]RZ[['v׀Pyi # @hXȺT$( a" ޅ- ,-1d黵ţgC|Sq!-a2!HtQ=l}j78O_Z鏇֛\ ۲5_kҴwœ{"i;%SAF :uw4Cr\yU]]-A *M\8q0~0o<n@rٚkn7oВO<<վϟWW>_o*Zʅ$c̺|?TO #Ð!C^7ѲiӦ{!n*@wSTZ+~"r n<n/!;wx",1@kkw߽oЂ'Ν;X0aζ Oe>eG}-SX%+3٧NOA]'ʲyjafU2()[q*B&ҸcRfWt*b%PZЭ#>4Ç'ڵkС Җ?bOjr6k%3)h #ߕWw( /MΏd_#J#T Sdԇ9,IU-H͞RiҮ= wE'_|Ϯ:t//xk]DΔz>}0!^CK cBL*@>|EȒ%K02ʊ}~sB!${>r;kFAf@HҞJ{E_ `%ރ)̥ZW1O SGF[u3yDL @ AT:&(QG?h0Mi)XSAʣG*---~7aYoJHgeSwZsdLvS 霭n9dqB:gCS&f)c #X/tM:C-];֖|̀ijB#>T.wG_k?'~<Z -a @" ?%ٳg   /`kv%EA* [GuB6oa[L?΅NlVc`OnDYgfJRF\9u7lLzDHGS5A| &@[CYÚi8DSqX -aĆ //RyybNNZs:Gw޽ ЩYٹNOǒKfnnf_|wIz1N<&LFhV<*#K1-taC~QBwH]$(aRt.4B )T?Ƙ>7qϻU0Ms$Xrt=`4d2~"s >W@BvZ厠éͅÚ6\<5M3_oYuި$ AK x.Ϧuz6+;;o #6k,,C-Ђ.:*?.m]P۽QB�h nPb$%nxk?>[>yTpj dPP B(#(NP IMME PPx@@%E&St*G| P#@PP"@P~P@1$ȑ7"@xu&/ÍnH B:F 7r!xp#@p>O2!CC1^.0D. !1Dti"vBHIk!B6]q !RD)J89^ޯ_rin/0p uu(^WkI#%"%G'N"Sv{AJ-|Bl1xg?$9Re>`0$;&UcHs {V3Od8[PjvUU} ,6zWe*0>rO8e93~hq(2~̑QRf*j, DzB[z]g2\F_@1pڇ`G(R8¢DKpD)'BX'B┐x%`o <"/?90DH#@x'F "tO|!C䲀ƙ_]"$`\enj9"q%B$mrBHIk !ܘ)g$DuGI$ȮuE}{(i3Zb_I=YfW9"$f@X.%BrŘbAeѾ''„$l܋2^'#V bYBbEIaT,\ Rţ,2II=MgGe DtiE7$}b}]G"$2~!B'N"$2~!B'NW^)B "tO|!CD闟"$`H B<#@ ^g|u!rY>1'C:/bՕEsDHYvK1!F`28<$}D˙9"$$UcbD%!}Ah({.ꠄBZ kX q#%SD !'ޱ9$$ q&D;"cED碒@E`;J1A9eEH*j)Nl.ծ5q_r62ݭ싪%NivL@U@HgAXCMZ78EA8 KJwvvS'8q!ǐJ SSKT QQCYBТZκ#D\B 9m2ʯUrlgYPCL,p96m$$|v BBhE@Өb}o|i#o|1 8L0]f;.!Da_.ZLwkK? Vac)Ur]UDYn "D!T"zB5C"~*?H,Cro6J[ *Fh-!DBw" cM24 v^+)|*!Bٌ= %D,.urg;4("2C@uuG_u4'BB@bj9!v+uife\󩻾&ꬂC u 5~/=f^?en?O eс RK{1*2ȝݾԭX[AH(Y"+-^*+CywϏ=#BlNGeysGӪ3ZD3SK,]0QB!7]j7M!#1Yj ʕ+TnW^, 7`H B<#@  x:D_~r>`!"OGO DA9E5 u "$!}l7gΜzkdrxTTT5 u 8(k֬鸾1 { $'BB&dќ<Gn޼'WG˟DHȄ5'S{%w #]pQrx9NCȽuIIr&}  Pޭ޵5;<_΄$, ~!n~{ kH3!՘a%?#EIwkRx: <΄`۵k׮%ߧ"B$ύ$ʍj{;Wk5|Gqe:\iߨKe΄߿qL.#EIH̦? 5 <^L޽{Ed#EF~}io?M/"cL5BO^VVdJ~͘1o 㻒88N1 GII r0.'S^'3gtă!m<8!$ЎrOr?@JSB9@749Foe By#D7^g2!F"3yn A+EG ##cٲe?pN"@#` 'T(S1VE__"IENDB`guidata-1.7.4/README0000666000000000000000000000241612626552272012537 0ustar rootrootguidata ======= Copyright © 2009-2015 CEA, Pierre Raybaut, licensed under the terms of the CECILL License (see ``Licence_CeCILL_V2-en.txt``). Overview -------- Based on the Qt Python binding module PyQt4, ``guidata`` is a Python library generating graphical user interfaces for easy dataset editing and display. It also provides helpers and application development tools for PyQt4. Generate GUIs to edit and display all kind of objects: - integers, floats, strings ; - ndarrays (NumPy's n-dimensional arrays) ; - etc. Application development tools: - configuration management - internationalization (``gettext``) - deployment tools - HDF5 I/O helpers - misc. utils Dependencies ------------ Requirements - Python >=2.6 or Python >=3.2 - PyQt4 4.x (x>=3 ; recommended x>=4) or PyQt5 5.x (x>=5) - spyderlib >=v2.0.10 (test launcher and array/dictionnary editors) Optional Python modules - h5py (HDF5 files I/O) - cx_Freeze or py2exe (application deployment on Windows platforms) Other optional modules gettext (text translation support) Recommended modules guiqwt >= 3.0 Installation ------------ From the source package: ``python setup.py install``guidata-1.7.4/setup.py0000666000000000000000000000760512627660532013376 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ guidata ======= Set of basic GUIs to edit and display objects of many kinds: - integers, floats, strings ; - ndarrays (NumPy's n-dimensional arrays) ; - etc. Copyright © 2009-2015 CEA Pierre Raybaut Licensed under the terms of the CECILL License (see guidata/__init__.py for details) """ from __future__ import print_function import setuptools # analysis:ignore from distutils.core import setup import sys import os import os.path as osp import shutil import atexit import subprocess from guidata.utils import get_subpackages, get_package_data LIBNAME = 'guidata' from guidata import __version__ as version DESCRIPTION = 'Automatic graphical user interfaces generation for easy '\ 'dataset editing and display' LONG_DESCRIPTION = '' KEYWORDS = '' CLASSIFIERS = ['Topic :: Scientific/Engineering'] if 'beta' in version or 'b' in version: CLASSIFIERS += ['Development Status :: 4 - Beta'] elif 'alpha' in version or 'a' in version: CLASSIFIERS += ['Development Status :: 3 - Alpha'] else: CLASSIFIERS += ['Development Status :: 5 - Production/Stable'] def build_chm_doc(libname): """Return CHM documentation file (on Windows only), which is copied under {PythonInstallDir}\Doc, hence allowing Spyder to add an entry for opening package documentation in "Help" menu. This has no effect on a source distribution.""" args = ''.join(sys.argv) if os.name == 'nt' and ('bdist' in args or 'build' in args): try: import sphinx # analysis:ignore except ImportError: print('Warning: `sphinx` is required to build documentation', file=sys.stderr) return hhc_base = r'C:\Program Files%s\HTML Help Workshop\hhc.exe' for hhc_exe in (hhc_base % '', hhc_base % ' (x86)'): if osp.isfile(hhc_exe): break else: print('Warning: `HTML Help Workshop` is required to build CHM '\ 'documentation file', file=sys.stderr) return doctmp_dir = 'doctmp' subprocess.call('sphinx-build -b htmlhelp doc %s' % doctmp_dir, shell=True) atexit.register(shutil.rmtree, osp.abspath(doctmp_dir)) fname = osp.abspath(osp.join(doctmp_dir, '%s.chm' % libname)) subprocess.call('"%s" %s' % (hhc_exe, fname), shell=True) if osp.isfile(fname): return fname else: print('Warning: CHM building process failed', file=sys.stderr) CHM_DOC = build_chm_doc(LIBNAME) setup(name=LIBNAME, version=version, description=DESCRIPTION, long_description=LONG_DESCRIPTION, packages=get_subpackages(LIBNAME), package_data={LIBNAME: get_package_data(LIBNAME, ('.png', '.svg', '.mo'))}, data_files=[(r'Doc', [CHM_DOC])] if CHM_DOC else [], entry_points={'gui_scripts': ['guidata-tests-py%d = guidata.tests:run'\ % sys.version_info.major,]}, extras_require = { 'Doc': ["Sphinx>=1.1"], }, author = "Pierre Raybaut", author_email = 'pierre.raybaut@gmail.com', url = 'https://github.com/PierreRaybaut/%s' % LIBNAME, license = 'CeCILL V2', classifiers=CLASSIFIERS + [ 'Operating System :: MacOS', 'Operating System :: Microsoft :: Windows', 'Operating System :: OS Independent', 'Operating System :: POSIX', 'Operating System :: Unix', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', ], ) guidata-1.7.4/changelog0000666000000000000000000003273712632324100013522 0ustar rootroot== Version 1.7.4 == Bug fixes: * Fixed compatibility issue with Python 3.5.1rc1 (Issue #32: RecursionError in userconfig.UserConfig.get) * HDF5Reader.read_object_list: fixed division by zero (when count was 1) * hdf5io: fixed Python3 compatibility issue with unicode_hdf type converter ---- == Version 1.7.3 == Features: * Added CHM documentation to wheel package * hdf5io: added support for a progress bar callback in "read_object_list" (this allows implementing a progress dialog widget showing the progress when reading an object list in an HDF5 file) Bug fixes: * Python 3 compatibility: fixed `hdf5io.HDF5Writer.write_object_list` method * data items: * StringItem: when `notempty` parameter was set to True, item value was not checked at startup (expected orange background) * disthelpers: * Supporting recent versions of SciPy, h5py and IPython * Fixed compatibility issue (workaround) with IPython on Python 2.7 (that is the "collection.sys cx_Freeze error") ---- == Version 1.7.2 == Bug fixes: * Fixed compatibility issues with old versions of Spyder (=1.1 guidata-1.7.4/guidata.egg-info/dependency_links.txt0000666000000000000000000000000112632324454021026 0ustar rootroot guidata-1.7.4/guidata.egg-info/PKG-INFO0000666000000000000000000000143512632324454016060 0ustar rootrootMetadata-Version: 1.1 Name: guidata Version: 1.7.4 Summary: Automatic graphical user interfaces generation for easy dataset editing and display Home-page: https://github.com/PierreRaybaut/guidata Author: Pierre Raybaut Author-email: pierre.raybaut@gmail.com License: CeCILL V2 Description: UNKNOWN Platform: UNKNOWN Classifier: Topic :: Scientific/Engineering Classifier: Development Status :: 5 - Production/Stable Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 guidata-1.7.4/guidata.egg-info/entry_points.txt0000666000000000000000000000006512632324454020257 0ustar rootroot[gui_scripts] guidata-tests-py2 = guidata.tests:run guidata-1.7.4/guidata.egg-info/SOURCES.txt0000666000000000000000000000732212632324456016652 0ustar rootrootLicence_CeCILL_V2-en.txt MANIFEST.in README changelog setup.py doc/basic_example.py doc/conf.py doc/development.rst doc/examples.rst doc/index.rst doc/installation.rst doc/overview.rst doc/_static/favicon.ico doc/images/basic_example.png doc/images/guidata.png doc/images/screenshots/__init__.png doc/images/screenshots/activable_dataset.png doc/images/screenshots/all_features.png doc/images/screenshots/all_items.png doc/images/screenshots/bool_selector.png doc/images/screenshots/datasetgroup.png doc/images/screenshots/editgroupbox.png doc/reference/configtools.rst doc/reference/dataset.rst doc/reference/disthelpers.rst doc/reference/index.rst doc/reference/qthelpers.rst doc/reference/userconfig.rst doc/reference/utils.rst guidata/__init__.py guidata/config.py guidata/configtools.py guidata/disthelpers.py guidata/gettext_helpers.py guidata/guitest.py guidata/hdf5io.py guidata/py3compat.py guidata/qthelpers.py guidata/qtwidgets.py guidata/userconfig.py guidata/userconfigio.py guidata/utils.py guidata.egg-info/PKG-INFO guidata.egg-info/SOURCES.txt guidata.egg-info/dependency_links.txt guidata.egg-info/entry_points.txt guidata.egg-info/requires.txt guidata.egg-info/top_level.txt guidata/dataset/__init__.py guidata/dataset/dataitems.py guidata/dataset/datatypes.py guidata/dataset/qtitemwidgets.py guidata/dataset/qtwidgets.py guidata/dataset/textedit.py guidata/images/apply.png guidata/images/arredit.png guidata/images/busy.png guidata/images/cell_edit.png guidata/images/copy.png guidata/images/delete.png guidata/images/dictedit.png guidata/images/edit.png guidata/images/exit.png guidata/images/expander_down.png guidata/images/expander_right.png guidata/images/file.png guidata/images/fileclose.png guidata/images/fileimport.png guidata/images/filenew.png guidata/images/fileopen.png guidata/images/filesave.png guidata/images/filesaveas.png guidata/images/guidata.svg guidata/images/max.png guidata/images/min.png guidata/images/none.png guidata/images/not_found.png guidata/images/python.png guidata/images/quickview.png guidata/images/save_all.png guidata/images/selection.png guidata/images/settings.png guidata/images/shape.png guidata/images/xmax.png guidata/images/xmin.png guidata/images/editors/edit.png guidata/images/editors/edit_add.png guidata/images/editors/editcopy.png guidata/images/editors/editdelete.png guidata/images/editors/editpaste.png guidata/images/editors/fileimport.png guidata/images/editors/filesave.png guidata/images/editors/imshow.png guidata/images/editors/insert.png guidata/images/editors/plot.png guidata/images/editors/rename.png guidata/images/filetypes/doc.png guidata/images/filetypes/gif.png guidata/images/filetypes/html.png guidata/images/filetypes/jpg.png guidata/images/filetypes/pdf.png guidata/images/filetypes/png.png guidata/images/filetypes/pps.png guidata/images/filetypes/ps.png guidata/images/filetypes/tar.png guidata/images/filetypes/tgz.png guidata/images/filetypes/tif.png guidata/images/filetypes/txt.png guidata/images/filetypes/xls.png guidata/images/filetypes/zip.png guidata/locale/fr/LC_MESSAGES/guidata.mo guidata/locale/fr/LC_MESSAGES/guidata.po guidata/qt/QtCore.py guidata/qt/QtDesigner.py guidata/qt/QtGui.py guidata/qt/QtSvg.py guidata/qt/QtWebKit.py guidata/qt/__init__.py guidata/qt/compat.py guidata/tests/__init__.py guidata/tests/activable_dataset.py guidata/tests/activable_items.py guidata/tests/all_features.py guidata/tests/all_items.py guidata/tests/bool_selector.py guidata/tests/callbacks.py guidata/tests/config.py guidata/tests/data.py guidata/tests/datasetgroup.py guidata/tests/disthelpers.py guidata/tests/editgroupbox.py guidata/tests/hdf5.py guidata/tests/inheritance.py guidata/tests/rotatedlabel.py guidata/tests/text.py guidata/tests/translations.py guidata/tests/userconfig_app.pyguidata-1.7.4/guidata.egg-info/top_level.txt0000666000000000000000000000001012632324454017501 0ustar rootrootguidata guidata-1.7.4/PKG-INFO0000666000000000000000000000143512632324456012752 0ustar rootrootMetadata-Version: 1.1 Name: guidata Version: 1.7.4 Summary: Automatic graphical user interfaces generation for easy dataset editing and display Home-page: https://github.com/PierreRaybaut/guidata Author: Pierre Raybaut Author-email: pierre.raybaut@gmail.com License: CeCILL V2 Description: UNKNOWN Platform: UNKNOWN Classifier: Topic :: Scientific/Engineering Classifier: Development Status :: 5 - Production/Stable Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 guidata-1.7.4/guidata/0000755000000000000000000000000012632514315013257 5ustar rootrootguidata-1.7.4/guidata/hdf5io.py0000666000000000000000000003122012630550056015011 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ Reader and Writer for the serialization of DataSets into HDF5 files """ from __future__ import print_function import sys from uuid import uuid1 import h5py import numpy as np from guidata.utils import utf8_to_unicode from guidata.userconfigio import BaseIOHandler, WriterMixin from guidata.py3compat import (PY2, PY3, is_binary_string, to_binary_string, to_text_string) class TypeConverter(object): def __init__(self, to_type, from_type=None): self._to_type = to_type if from_type: self._from_type = from_type else: self._from_type = to_type def to_hdf(self, value): try: return self._to_type(value) except: print("ERR", repr(value), file=sys.stderr) raise def from_hdf(self, value): return self._from_type(value) if PY2: unicode_hdf = TypeConverter(lambda x: x.encode("utf-8"), utf8_to_unicode) else: unicode_hdf = TypeConverter(lambda x: x.encode("utf-8"), lambda x: to_text_string(x, encoding='utf-8')) int_hdf = TypeConverter(int) class Attr(object): """Helper class representing class attribute that should be saved/restored to/from a corresponding HDF5 attribute hdf_name : name of the attribute in the HDF5 file struct_name : name of the attribute in the object (default to hdf_name) type : attribute type (guess it if None) optional : indicates whether we should fail if the attribute is not present """ def __init__(self, hdf_name, struct_name=None, type=None, optional=False): self.hdf_name = hdf_name if struct_name is None: struct_name = hdf_name self.struct_name = struct_name self.type = type self.optional = optional def get_value(self, struct): if self.optional: return getattr(struct, self.struct_name, None) else: return getattr(struct, self.struct_name) def set_value(self, struct, value): setattr(struct, self.struct_name, value) def save(self, group, struct): value = self.get_value(struct) if self.optional and value is None: #print ".-", self.hdf_name, value if self.hdf_name in group.attrs: del group.attrs[self.hdf_name] return if self.type is not None: value = self.type.to_hdf(value) #print ".", self.hdf_name, value, self.optional try: group.attrs[self.hdf_name] = value except: print("ERROR saving:", repr(value), "into", self.hdf_name, file=sys.stderr) raise def load(self, group, struct): #print "LoadAttr:", group, self.hdf_name if self.optional: if self.hdf_name not in group.attrs: self.set_value(struct, None) return try: value = group.attrs[self.hdf_name] except KeyError: raise KeyError('Unable to locate attribute %s' % self.hdf_name) if self.type is not None: value = self.type.from_hdf(value) self.set_value(struct, value) def createdset(group, name, value): group.create_dataset(name, compression=None, #compression_opts=3, data=value) class Dset(Attr): """ Generic load/save for an hdf5 dataset: scalar=float -> used to convert the value when it is scalar """ def __init__(self, hdf_name, struct_name=None, type=None, scalar=None, optional=False): Attr.__init__(self, hdf_name, struct_name, type, optional) self.scalar = scalar def save(self, group, struct): value = self.get_value(struct) if isinstance(value, float): value = np.float64(value) elif isinstance(value, int): value = np.int32(value) if value is None or value.size==0: value = np.array([0.0]) if value.shape == (): value = value.reshape( (1,) ) group.require_dataset(self.hdf_name, shape=value.shape, dtype=value.dtype, data=value, compression="gzip", compression_opts=1) def load(self, group, struct): if self.optional: if self.hdf_name not in group: self.set_value(struct, None) return try: value = group[self.hdf_name][...] except KeyError: raise KeyError('Unable to locate dataset %s' % self.hdf_name) if self.scalar is not None: value = self.scalar(value) self.set_value(struct, value) class Dlist(Dset): def get_value(self, struct): return np.array( getattr(struct, self.struct_name) ) def set_value(self, struct, value): setattr(struct, self.struct_name, list(value)) #============================================================================== # Base HDF5 Store object: do not break API compatibility here as this class is # used in various critical projects for saving/loading application data #============================================================================== class H5Store(object): def __init__(self, filename): self.filename = filename self.h5 = None def open(self, mode="a"): """Open an hdf5 file""" if self.h5: return self.h5 try: self.h5 = h5py.File(self.filename, mode=mode) except Exception: print("Error trying to load:", self.filename, "in mode:", mode, file=sys.stderr) raise return self.h5 def close(self): if self.h5: self.h5.close() self.h5 = None def generic_save(self, parent, source, structure): """save the data from source into the file using 'structure' as a descriptor. structure is a list of Attribute Descriptor (Attr, Dset, Dlist or anything with a save interface) that describe the conversion of data and the name of the attribute in the source and in the file """ for instr in structure: instr.save(parent, source) def generic_load(self, parent, dest, structure): """load the data from the file into dest using 'structure' as a descriptor. structure is the same as in generic_save """ for instr in structure: try: instr.load(parent, dest) except Exception: print("Error loading HDF5 item:", instr.hdf_name, file=sys.stderr) raise #============================================================================== # HDF5 reader/writer: do not break API compatibility here as this class is # used in various critical projects for saving/loading application data and # in guiqwt for saving/loading plot items. #============================================================================== class HDF5Handler(H5Store, BaseIOHandler): """Base HDF5 I/O Handler object""" def __init__(self, filename): H5Store.__init__(self, filename) self.option = [] def get_parent_group(self): parent = self.h5 for option in self.option[:-1]: parent = parent.require_group(option) return parent class HDF5Writer(HDF5Handler, WriterMixin): """Writer for HDF5 files""" def __init__(self, filename): super(HDF5Writer, self).__init__(filename) self.open("w") def write_any(self, val): group = self.get_parent_group() group.attrs[self.option[-1]] = val write_int = write_float = write_any def write_bool(self, val): self.write_int(int(val)) write_str = write_any def write_unicode(self, val): group = self.get_parent_group() group.attrs[self.option[-1]] = val.encode("utf-8") if PY3: write_unicode = write_str def write_array(self, val): group = self.get_parent_group() group[self.option[-1]] = val write_sequence = write_any def write_none(self): group = self.get_parent_group() group.attrs[self.option[-1]] = "" def write_object_list(self, seq, group_name): """Write object sequence in group. Objects must implement the DataSet-like `serialize` method""" with self.group(group_name): if seq is None: self.write_none() else: ids = [] for obj in seq: guid = to_binary_string(str(uuid1())) ids.append(guid) with self.group(guid): if obj is None: self.write_none() else: obj.serialize(self) self.write(ids, 'IDs') class HDF5Reader(HDF5Handler): """Reader for HDF5 files""" def __init__(self, filename): super(HDF5Reader, self).__init__(filename) self.open("r") def read(self, group_name=None, func=None, instance=None): """Read value within current group or group_name. Optional argument `instance` is an object which implements the DataSet-like `deserialize` method.""" if group_name: self.begin(group_name) if instance is None: if func is None: func = self.read_any val = func() else: group = self.get_parent_group() if group_name in group.attrs: # This is an attribute (not a group), meaning that # the object was None when deserializing it val = None else: instance.deserialize(self) val = instance if group_name: self.end(group_name) return val def read_any(self): group = self.get_parent_group() value = group.attrs[self.option[-1]] if is_binary_string(value): return value.decode("utf-8") else: return value def read_bool(self): val = self.read_any() if val != '': return bool(val) def read_int(self): val = self.read_any() if val != '': return int(val) def read_float(self): val = self.read_any() if val != '': return float(val) read_unicode = read_str = read_any def read_array(self): group = self.get_parent_group() return group[self.option[-1]][...] def read_sequence(self): group = self.get_parent_group() return list(group.attrs[self.option[-1]]) def read_object_list(self, group_name, klass, progress_callback=None): """Read object sequence in group. Objects must implement the DataSet-like `deserialize` method. `klass` is the object class which constructor requires no argument. progress_callback: if not None, this function is called with an integer argument (progress: 0 --> 100). Function returns the `cancel` state (True: progress dialog has been canceled, False otherwise) """ with self.group(group_name): try: ids = self.read('IDs', func=self.read_sequence) except ValueError: # None was saved instead of list of objects self.end('IDs') return seq = [] count = len(ids) for idx, name in enumerate(ids): if progress_callback is not None: if progress_callback(int(100*float(idx)/count)): break with self.group(name): group = self.get_parent_group() if name in group.attrs: # This is an attribute (not a group), meaning that # the object was None when deserializing it obj = None else: obj = klass() obj.deserialize(self) seq.append(obj) return seq read_none = read_any guidata-1.7.4/guidata/__init__.py0000666000000000000000000006277112632323566015417 0ustar rootroot# -*- coding: utf-8 -*- """ guidata ======= Based on the Qt library (``PyQt4`` and ``PyQt5`` are currently supported), `guidata` is a Python library generating graphical user interfaces for easy dataset editing and display. It also provides helpers and application development tools for PyQt. External resources: * Python Package Index: `PyPI`_ * Bug reports and feature requests: `GitHub`_ * Help, support and discussions around the project: `GoogleGroup`_ .. _PyPI: https://pypi.python.org/pypi/guidata .. _GitHub: https://github.com/PierreRaybaut/guidata .. _GoogleGroup: http://groups.google.fr/group/guidata_guiqwt """ __version__ = '1.7.4' # Dear (Debian, RPM, ...) package makers, please feel free to customize the # following path to module's data (images) and translations: DATAPATH = LOCALEPATH = '' # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License (see below) # LICENSE TERMS: # ------------- # # CeCILL FREE SOFTWARE LICENSE AGREEMENT # # # Notice # # This Agreement is a Free Software license agreement that is the result # of discussions between its authors in order to ensure compliance with # the two main principles guiding its drafting: # * firstly, compliance with the principles governing the distribution # of Free Software: access to source code, broad rights granted to # users, # * secondly, the election of a governing law, French law, with which # it is conformant, both as regards the law of torts and # intellectual property law, and the protection that it offers to # both authors and holders of the economic rights over software. # # The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre]) # license are: # # Commissariat à l'Energie Atomique - CEA, a public scientific, technical # and industrial research establishment, having its principal place of # business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France. # # Centre National de la Recherche Scientifique - CNRS, a public scientific # and technological establishment, having its principal place of business # at 3 rue Michel-Ange, 75794 Paris cedex 16, France. # # Institut National de Recherche en Informatique et en Automatique - # INRIA, a public scientific and technological establishment, having its # principal place of business at Domaine de Voluceau, Rocquencourt, BP # 105, 78153 Le Chesnay cedex, France. # # # Preamble # # The purpose of this Free Software license agreement is to grant users # the right to modify and redistribute the software governed by this # license within the framework of an open source distribution model. # # The exercising of these rights is conditional upon certain obligations # for users so as to preserve this status for all subsequent redistributions. # # In consideration of access to the source code and the rights to copy, # modify and redistribute granted by the license, users are provided only # with a limited warranty and the software's author, the holder of the # economic rights, and the successive licensors only have limited liability. # # In this respect, the risks associated with loading, using, modifying # and/or developing or reproducing the software by the user are brought to # the user's attention, given its Free Software status, which may make it # complicated to use, with the result that its use is reserved for # developers and experienced professionals having in-depth computer # knowledge. Users are therefore encouraged to load and test the # suitability of the software as regards their requirements in conditions # enabling the security of their systems and/or data to be ensured and, # more generally, to use and operate it in the same conditions of # security. This Agreement may be freely reproduced and published, # provided it is not altered, and that no provisions are either added or # removed herefrom. # # This Agreement may apply to any or all software for which the holder of # the economic rights decides to submit the use thereof to its provisions. # # # Article 1 - DEFINITIONS # # For the purpose of this Agreement, when the following expressions # commence with a capital letter, they shall have the following meaning: # # Agreement: means this license agreement, and its possible subsequent # versions and annexes. # # Software: means the software in its Object Code and/or Source Code form # and, where applicable, its documentation, "as is" when the Licensee # accepts the Agreement. # # Initial Software: means the Software in its Source Code and possibly its # Object Code form and, where applicable, its documentation, "as is" when # it is first distributed under the terms and conditions of the Agreement. # # Modified Software: means the Software modified by at least one # Contribution. # # Source Code: means all the Software's instructions and program lines to # which access is required so as to modify the Software. # # Object Code: means the binary files originating from the compilation of # the Source Code. # # Holder: means the holder(s) of the economic rights over the Initial # Software. # # Licensee: means the Software user(s) having accepted the Agreement. # # Contributor: means a Licensee having made at least one Contribution. # # Licensor: means the Holder, or any other individual or legal entity, who # distributes the Software under the Agreement. # # Contribution: means any or all modifications, corrections, translations, # adaptations and/or new functions integrated into the Software by any or # all Contributors, as well as any or all Internal Modules. # # Module: means a set of sources files including their documentation that # enables supplementary functions or services in addition to those offered # by the Software. # # External Module: means any or all Modules, not derived from the # Software, so that this Module and the Software run in separate address # spaces, with one calling the other when they are run. # # Internal Module: means any or all Module, connected to the Software so # that they both execute in the same address space. # # GNU GPL: means the GNU General Public License version 2 or any # subsequent version, as published by the Free Software Foundation Inc. # # Parties: mean both the Licensee and the Licensor. # # These expressions may be used both in singular and plural form. # # # Article 2 - PURPOSE # # The purpose of the Agreement is the grant by the Licensor to the # Licensee of a non-exclusive, transferable and worldwide license for the # Software as set forth in Article 5 hereinafter for the whole term of the # protection granted by the rights over said Software. # # # Article 3 - ACCEPTANCE # # 3.1 The Licensee shall be deemed as having accepted the terms and # conditions of this Agreement upon the occurrence of the first of the # following events: # # * (i) loading the Software by any or all means, notably, by # downloading from a remote server, or by loading from a physical # medium; # * (ii) the first time the Licensee exercises any of the rights # granted hereunder. # # 3.2 One copy of the Agreement, containing a notice relating to the # characteristics of the Software, to the limited warranty, and to the # fact that its use is restricted to experienced users has been provided # to the Licensee prior to its acceptance as set forth in Article 3.1 # hereinabove, and the Licensee hereby acknowledges that it has read and # understood it. # # # Article 4 - EFFECTIVE DATE AND TERM # # # 4.1 EFFECTIVE DATE # # The Agreement shall become effective on the date when it is accepted by # the Licensee as set forth in Article 3.1. # # # 4.2 TERM # # The Agreement shall remain in force for the entire legal term of # protection of the economic rights over the Software. # # # Article 5 - SCOPE OF RIGHTS GRANTED # # The Licensor hereby grants to the Licensee, who accepts, the following # rights over the Software for any or all use, and for the term of the # Agreement, on the basis of the terms and conditions set forth hereinafter. # # Besides, if the Licensor owns or comes to own one or more patents # protecting all or part of the functions of the Software or of its # components, the Licensor undertakes not to enforce the rights granted by # these patents against successive Licensees using, exploiting or # modifying the Software. If these patents are transferred, the Licensor # undertakes to have the transferees subscribe to the obligations set # forth in this paragraph. # # # 5.1 RIGHT OF USE # # The Licensee is authorized to use the Software, without any limitation # as to its fields of application, with it being hereinafter specified # that this comprises: # # 1. permanent or temporary reproduction of all or part of the Software # by any or all means and in any or all form. # # 2. loading, displaying, running, or storing the Software on any or # all medium. # # 3. entitlement to observe, study or test its operation so as to # determine the ideas and principles behind any or all constituent # elements of said Software. This shall apply when the Licensee # carries out any or all loading, displaying, running, transmission # or storage operation as regards the Software, that it is entitled # to carry out hereunder. # # # 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS # # The right to make Contributions includes the right to translate, adapt, # arrange, or make any or all modifications to the Software, and the right # to reproduce the resulting software. # # The Licensee is authorized to make any or all Contributions to the # Software provided that it includes an explicit notice that it is the # author of said Contribution and indicates the date of the creation thereof. # # # 5.3 RIGHT OF DISTRIBUTION # # In particular, the right of distribution includes the right to publish, # transmit and communicate the Software to the general public on any or # all medium, and by any or all means, and the right to market, either in # consideration of a fee, or free of charge, one or more copies of the # Software by any means. # # The Licensee is further authorized to distribute copies of the modified # or unmodified Software to third parties according to the terms and # conditions set forth hereinafter. # # # 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION # # The Licensee is authorized to distribute true copies of the Software in # Source Code or Object Code form, provided that said distribution # complies with all the provisions of the Agreement and is accompanied by: # # 1. a copy of the Agreement, # # 2. a notice relating to the limitation of both the Licensor's # warranty and liability as set forth in Articles 8 and 9, # # and that, in the event that only the Object Code of the Software is # redistributed, the Licensee allows future Licensees unhindered access to # the full Source Code of the Software by indicating how to access it, it # being understood that the additional cost of acquiring the Source Code # shall not exceed the cost of transferring the data. # # # 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE # # When the Licensee makes a Contribution to the Software, the terms and # conditions for the distribution of the resulting Modified Software # become subject to all the provisions of this Agreement. # # The Licensee is authorized to distribute the Modified Software, in # source code or object code form, provided that said distribution # complies with all the provisions of the Agreement and is accompanied by: # # 1. a copy of the Agreement, # # 2. a notice relating to the limitation of both the Licensor's # warranty and liability as set forth in Articles 8 and 9, # # and that, in the event that only the object code of the Modified # Software is redistributed, the Licensee allows future Licensees # unhindered access to the full source code of the Modified Software by # indicating how to access it, it being understood that the additional # cost of acquiring the source code shall not exceed the cost of # transferring the data. # # # 5.3.3 DISTRIBUTION OF EXTERNAL MODULES # # When the Licensee has developed an External Module, the terms and # conditions of this Agreement do not apply to said External Module, that # may be distributed under a separate license agreement. # # # 5.3.4 COMPATIBILITY WITH THE GNU GPL # # The Licensee can include a code that is subject to the provisions of one # of the versions of the GNU GPL in the Modified or unmodified Software, # and distribute that entire code under the terms of the same version of # the GNU GPL. # # The Licensee can include the Modified or unmodified Software in a code # that is subject to the provisions of one of the versions of the GNU GPL, # and distribute that entire code under the terms of the same version of # the GNU GPL. # # # Article 6 - INTELLECTUAL PROPERTY # # # 6.1 OVER THE INITIAL SOFTWARE # # The Holder owns the economic rights over the Initial Software. Any or # all use of the Initial Software is subject to compliance with the terms # and conditions under which the Holder has elected to distribute its work # and no one shall be entitled to modify the terms and conditions for the # distribution of said Initial Software. # # The Holder undertakes that the Initial Software will remain ruled at # least by this Agreement, for the duration set forth in Article 4.2. # # # 6.2 OVER THE CONTRIBUTIONS # # The Licensee who develops a Contribution is the owner of the # intellectual property rights over this Contribution as defined by # applicable law. # # # 6.3 OVER THE EXTERNAL MODULES # # The Licensee who develops an External Module is the owner of the # intellectual property rights over this External Module as defined by # applicable law and is free to choose the type of agreement that shall # govern its distribution. # # # 6.4 JOINT PROVISIONS # # The Licensee expressly undertakes: # # 1. not to remove, or modify, in any manner, the intellectual property # notices attached to the Software; # # 2. to reproduce said notices, in an identical manner, in the copies # of the Software modified or not. # # The Licensee undertakes not to directly or indirectly infringe the # intellectual property rights of the Holder and/or Contributors on the # Software and to take, where applicable, vis-à-vis its staff, any and all # measures required to ensure respect of said intellectual property rights # of the Holder and/or Contributors. # # # Article 7 - RELATED SERVICES # # 7.1 Under no circumstances shall the Agreement oblige the Licensor to # provide technical assistance or maintenance services for the Software. # # However, the Licensor is entitled to offer this type of services. The # terms and conditions of such technical assistance, and/or such # maintenance, shall be set forth in a separate instrument. Only the # Licensor offering said maintenance and/or technical assistance services # shall incur liability therefor. # # 7.2 Similarly, any Licensor is entitled to offer to its licensees, under # its sole responsibility, a warranty, that shall only be binding upon # itself, for the redistribution of the Software and/or the Modified # Software, under terms and conditions that it is free to decide. Said # warranty, and the financial terms and conditions of its application, # shall be subject of a separate instrument executed between the Licensor # and the Licensee. # # # Article 8 - LIABILITY # # 8.1 Subject to the provisions of Article 8.2, the Licensee shall be # entitled to claim compensation for any direct loss it may have suffered # from the Software as a result of a fault on the part of the relevant # Licensor, subject to providing evidence thereof. # # 8.2 The Licensor's liability is limited to the commitments made under # this Agreement and shall not be incurred as a result of in particular: # (i) loss due the Licensee's total or partial failure to fulfill its # obligations, (ii) direct or consequential loss that is suffered by the # Licensee due to the use or performance of the Software, and (iii) more # generally, any consequential loss. In particular the Parties expressly # agree that any or all pecuniary or business loss (i.e. loss of data, # loss of profits, operating loss, loss of customers or orders, # opportunity cost, any disturbance to business activities) or any or all # legal proceedings instituted against the Licensee by a third party, # shall constitute consequential loss and shall not provide entitlement to # any or all compensation from the Licensor. # # # Article 9 - WARRANTY # # 9.1 The Licensee acknowledges that the scientific and technical # state-of-the-art when the Software was distributed did not enable all # possible uses to be tested and verified, nor for the presence of # possible defects to be detected. In this respect, the Licensee's # attention has been drawn to the risks associated with loading, using, # modifying and/or developing and reproducing the Software which are # reserved for experienced users. # # The Licensee shall be responsible for verifying, by any or all means, # the suitability of the product for its requirements, its good working # order, and for ensuring that it shall not cause damage to either persons # or properties. # # 9.2 The Licensor hereby represents, in good faith, that it is entitled # to grant all the rights over the Software (including in particular the # rights set forth in Article 5). # # 9.3 The Licensee acknowledges that the Software is supplied "as is" by # the Licensor without any other express or tacit warranty, other than # that provided for in Article 9.2 and, in particular, without any warranty # as to its commercial value, its secured, safe, innovative or relevant # nature. # # Specifically, the Licensor does not warrant that the Software is free # from any error, that it will operate without interruption, that it will # be compatible with the Licensee's own equipment and software # configuration, nor that it will meet the Licensee's requirements. # # 9.4 The Licensor does not either expressly or tacitly warrant that the # Software does not infringe any third party intellectual property right # relating to a patent, software or any other property right. Therefore, # the Licensor disclaims any and all liability towards the Licensee # arising out of any or all proceedings for infringement that may be # instituted in respect of the use, modification and redistribution of the # Software. Nevertheless, should such proceedings be instituted against # the Licensee, the Licensor shall provide it with technical and legal # assistance for its defense. Such technical and legal assistance shall be # decided on a case-by-case basis between the relevant Licensor and the # Licensee pursuant to a memorandum of understanding. The Licensor # disclaims any and all liability as regards the Licensee's use of the # name of the Software. No warranty is given as regards the existence of # prior rights over the name of the Software or as regards the existence # of a trademark. # # # Article 10 - TERMINATION # # 10.1 In the event of a breach by the Licensee of its obligations # hereunder, the Licensor may automatically terminate this Agreement # thirty (30) days after notice has been sent to the Licensee and has # remained ineffective. # # 10.2 A Licensee whose Agreement is terminated shall no longer be # authorized to use, modify or distribute the Software. However, any # licenses that it may have granted prior to termination of the Agreement # shall remain valid subject to their having been granted in compliance # with the terms and conditions hereof. # # # Article 11 - MISCELLANEOUS # # # 11.1 EXCUSABLE EVENTS # # Neither Party shall be liable for any or all delay, or failure to # perform the Agreement, that may be attributable to an event of force # majeure, an act of God or an outside cause, such as defective # functioning or interruptions of the electricity or telecommunications # networks, network paralysis following a virus attack, intervention by # government authorities, natural disasters, water damage, earthquakes, # fire, explosions, strikes and labor unrest, war, etc. # # 11.2 Any failure by either Party, on one or more occasions, to invoke # one or more of the provisions hereof, shall under no circumstances be # interpreted as being a waiver by the interested Party of its right to # invoke said provision(s) subsequently. # # 11.3 The Agreement cancels and replaces any or all previous agreements, # whether written or oral, between the Parties and having the same # purpose, and constitutes the entirety of the agreement between said # Parties concerning said purpose. No supplement or modification to the # terms and conditions hereof shall be effective as between the Parties # unless it is made in writing and signed by their duly authorized # representatives. # # 11.4 In the event that one or more of the provisions hereof were to # conflict with a current or future applicable act or legislative text, # said act or legislative text shall prevail, and the Parties shall make # the necessary amendments so as to comply with said act or legislative # text. All other provisions shall remain effective. Similarly, invalidity # of a provision of the Agreement, for any reason whatsoever, shall not # cause the Agreement as a whole to be invalid. # # # 11.5 LANGUAGE # # The Agreement is drafted in both French and English and both versions # are deemed authentic. # # # Article 12 - NEW VERSIONS OF THE AGREEMENT # # 12.1 Any person is authorized to duplicate and distribute copies of this # Agreement. # # 12.2 So as to ensure coherence, the wording of this Agreement is # protected and may only be modified by the authors of the License, who # reserve the right to periodically publish updates or new versions of the # Agreement, each with a separate number. These subsequent versions may # address new issues encountered by Free Software. # # 12.3 Any Software distributed under a given version of the Agreement may # only be subsequently distributed under the same version of the Agreement # or a subsequent version, subject to the provisions of Article 5.3.4. # # # Article 13 - GOVERNING LAW AND JURISDICTION # # 13.1 The Agreement is governed by French law. The Parties agree to # endeavor to seek an amicable solution to any disagreements or disputes # that may arise during the performance of the Agreement. # # 13.2 Failing an amicable solution within two (2) months as from their # occurrence, and unless emergency proceedings are necessary, the # disagreements or disputes shall be referred to the Paris Courts having # jurisdiction, by the more diligent Party. # # # Version 2.0 dated 2006-09-05. import guidata.config def qapplication(): """ Return QApplication instance Creates it if it doesn't already exist """ from guidata.qt.QtGui import QApplication app = QApplication.instance() if not app: app = QApplication([]) install_translator(app) return app QT_TRANSLATOR = None def install_translator(qapp): """Install Qt translator to the QApplication instance""" global QT_TRANSLATOR if QT_TRANSLATOR is None: from guidata.qt.QtCore import QLocale, QTranslator, QLibraryInfo locale = QLocale.system().name() # Qt-specific translator qt_translator = QTranslator() paths = QLibraryInfo.location(QLibraryInfo.TranslationsPath) if qt_translator.load("qt_"+locale, paths): QT_TRANSLATOR = qt_translator # Keep reference alive if QT_TRANSLATOR is not None: qapp.installTranslator(QT_TRANSLATOR) guidata-1.7.4/guidata/qt/0000755000000000000000000000000012632514315013703 5ustar rootrootguidata-1.7.4/guidata/qt/__init__.py0000666000000000000000000000566612621672440016037 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2011-2012 Pierre Raybaut # © 2012-2014 anatoly techtonik # Licensed under the terms of the MIT License # (see spyderlib/__init__.py for details) """Compatibility package (PyQt4/PyQt5/PySide)""" from __future__ import print_function import os os.environ.setdefault('QT_API', 'pyqt') assert os.environ['QT_API'] in ('pyqt5', 'pyqt', 'pyside') API = os.environ['QT_API'] API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyside': 'PySide'}[API] if API == 'pyqt': # Spyder 2.3 is compatible with both #1 and #2 PyQt API, # but to avoid issues with IPython and other Qt plugins # we choose to support only API #2 for 2.4+ import sip try: sip.setapi('QString', 2) sip.setapi('QVariant', 2) sip.setapi('QDate', 2) sip.setapi('QDateTime', 2) sip.setapi('QTextStream', 2) sip.setapi('QTime', 2) sip.setapi('QUrl', 2) except ValueError: import sys if 'spyderlib.spyder' in sys.modules: # Spyder v2 is initializing: it's safe to ignore this exception from spyderlib import __version__ as spyder_ver if int(spyder_ver.split('.')[1]) < 3: print("Warning: deprecated version of Spyder, please upgrade!", file=sys.stderr) else: raise except AttributeError: # PyQt < v4.6. The actual check is done by requirements.check_qt() # call from spyder.py pass try: from PyQt4.QtCore import PYQT_VERSION_STR as __version__ # analysis:ignore except ImportError: # Trying PyQt5 before switching to PySide (at this point, PyQt4 may # not be installed but PyQt5 or Pyside could still be if the QT_API # environment variable hasn't been set-up) try: import PyQt5 # analysis:ignore API = os.environ['QT_API'] = 'pyqt5' API_NAME = 'PyQt5' except ImportError: API = os.environ['QT_API'] = 'pyside' API_NAME = 'PySide' else: is_old_pyqt = __version__.startswith(('4.4', '4.5', '4.6', '4.7')) is_pyqt46 = __version__.startswith('4.6') import sip try: API_NAME += (" (API v%d)" % sip.getapi('QString')) except AttributeError: pass from PyQt4 import uic # analysis:ignore PYQT5 = False if API == 'pyqt5': try: from PyQt5.QtCore import PYQT_VERSION_STR as __version__ from PyQt5 import uic # analysis:ignore PYQT5 = True is_old_pyqt = is_pyqt46 = False except ImportError: pass if API == 'pyside': try: from PySide import __version__ # analysis:ignore except ImportError: raise ImportError("Spyder requires PySide or PyQt to be installed") else: is_old_pyqt = is_pyqt46 = False guidata-1.7.4/guidata/qt/QtCore.py0000666000000000000000000000265612601727200015462 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2011 Pierre Raybaut # Licensed under the terms of the MIT License # (copied from Spyder source code [spyderlib.qt]) import os if os.environ['QT_API'] == 'pyqt5': from PyQt5.QtCore import * # analysis:ignore from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import pyqtSignal as Signal from PyQt5.QtCore import pyqtSlot as Slot from PyQt5.QtCore import pyqtProperty as Property from PyQt5.QtCore import QT_VERSION_STR as __version__ elif os.environ['QT_API'] == 'pyqt': from PyQt4.QtCore import * # analysis:ignore from PyQt4.Qt import QCoreApplication # analysis:ignore from PyQt4.Qt import Qt # analysis:ignore from PyQt4.QtCore import pyqtSignal as Signal # analysis:ignore from PyQt4.QtCore import pyqtSlot as Slot # analysis:ignore from PyQt4.QtCore import pyqtProperty as Property # analysis:ignore from PyQt4.QtCore import QT_VERSION_STR as __version__ # analysis:ignore # Forces new modules written by PyQt4 developers to be PyQt5-compatible del SIGNAL, SLOT else: import PySide.QtCore __version__ = PySide.QtCore.__version__ # analysis:ignore from PySide.QtCore import * # analysis:ignore guidata-1.7.4/guidata/qt/QtGui.py0000666000000000000000000000171012601727200015304 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2011 Pierre Raybaut # Licensed under the terms of the MIT License # (copied from Spyder source code [spyderlib.qt]) import os if os.environ['QT_API'] == 'pyqt5': from PyQt5.QtCore import QSortFilterProxyModel # analysis:ignore from PyQt5.QtPrintSupport import (QPrinter, QPrintDialog, # analysis:ignore QAbstractPrintDialog) from PyQt5.QtPrintSupport import QPrintPreviewDialog # analysis:ignore from PyQt5.QtGui import * # analysis:ignore from PyQt5.QtWidgets import * # analysis:ignore elif os.environ['QT_API'] == 'pyqt': from PyQt4.Qt import QKeySequence, QTextCursor # analysis:ignore from PyQt4.QtGui import * # analysis:ignore else: from PySide.QtGui import * # analysis:ignore guidata-1.7.4/guidata/qt/compat.py0000666000000000000000000002060112601727200015536 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2011-2012 Pierre Raybaut # Licensed under the terms of the MIT License # (see spyderlib/__init__.py for details) """ spyderlib.qt.compat ------------------- Transitional module providing compatibility functions intended to help migrating from PyQt to PySide. This module should be fully compatible with: * PyQt >=v4.4 * both PyQt API #1 and API #2 * PySide """ from __future__ import print_function import os import sys import collections from guidata.qt.QtGui import QFileDialog from guidata.py3compat import is_text_string, to_text_string, TEXT_TYPES #============================================================================== # QVariant conversion utilities #============================================================================== PYQT_API_1 = False if os.environ['QT_API'] == 'pyqt': import sip try: PYQT_API_1 = sip.getapi('QVariant') == 1 # PyQt API #1 except AttributeError: # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" # Calling QFileDialog static method if sys.platform == "win32": # On Windows platforms: redirect standard outputs _temp1, _temp2 = sys.stdout, sys.stderr sys.stdout, sys.stderr = None, None try: result = QFileDialog.getExistingDirectory(parent, caption, basedir, options) finally: if sys.platform == "win32": # On Windows platforms: restore standard outputs sys.stdout, sys.stderr = _temp1, _temp2 if not is_text_string(result): # PyQt API #1 result = to_text_string(result) return result def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', filters='', selectedfilter='', options=None): if options is None: options = QFileDialog.Options(0) try: # PyQt =v4.6 QString = None # analysis:ignore tuple_returned = True try: # PyQt >=v4.6 func = getattr(QFileDialog, attr+'AndFilter') except AttributeError: # PySide or PyQt =v4.6 output, selectedfilter = result else: # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" return _qfiledialog_wrapper('getOpenFileName', parent=parent, caption=caption, basedir=basedir, filters=filters, selectedfilter=selectedfilter, options=options) def getopenfilenames(parent=None, caption='', basedir='', filters='', selectedfilter='', options=None): """Wrapper around QtGui.QFileDialog.getOpenFileNames static method Returns a tuple (filenames, selectedfilter) -- when dialog box is canceled, returns a tuple (empty list, empty string) Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" return _qfiledialog_wrapper('getOpenFileNames', parent=parent, caption=caption, basedir=basedir, filters=filters, selectedfilter=selectedfilter, options=options) def getsavefilename(parent=None, caption='', basedir='', filters='', selectedfilter='', options=None): """Wrapper around QtGui.QFileDialog.getSaveFileName static method Returns a tuple (filename, selectedfilter) -- when dialog box is canceled, returns a tuple of empty strings Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" return _qfiledialog_wrapper('getSaveFileName', parent=parent, caption=caption, basedir=basedir, filters=filters, selectedfilter=selectedfilter, options=options) if __name__ == '__main__': import guidata _app = guidata.qapplication() print(repr(getexistingdirectory())) print(repr(getopenfilename(filters='*.py;;*.txt'))) print(repr(getopenfilenames(filters='*.py;;*.txt'))) print(repr(getsavefilename(filters='*.py;;*.txt'))) sys.exit() guidata-1.7.4/guidata/qt/QtSvg.py0000666000000000000000000000075612601727200015330 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2012 Pierre Raybaut # Licensed under the terms of the MIT License # (see spyderlib/__init__.py for details) import os if os.environ['QT_API'] == 'pyqt5': from PyQt5.QtSvg import * # analysis:ignore elif os.environ['QT_API'] == 'pyqt': from PyQt4.QtSvg import * # analysis:ignore else: from PySide.QtSvg import * # analysis:ignore guidata-1.7.4/guidata/qt/QtDesigner.py0000666000000000000000000000064112601727200016322 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2012 Pierre Raybaut # Licensed under the terms of the MIT License # (see spyderlib/__init__.py for details) import os if os.environ['QT_API'] == 'pyqt5': from PyQt5.QtDesigner import * # analysis:ignore elif os.environ['QT_API'] == 'pyqt': from PyQt4.QtDesigner import * # analysis:ignore else: from PySide.QtDesigner import * # analysis:ignore guidata-1.7.4/guidata/qt/QtWebKit.py0000666000000000000000000000115612601727200015751 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2011 Pierre Raybaut # Licensed under the terms of the MIT License # (see spyderlib/__init__.py for details) import os if os.environ['QT_API'] == 'pyqt5': from PyQt5.QtWebKitWidgets import QWebPage, QWebView # analysis:ignore from PyQt5.QtWebKit import QWebSettings # analysis:ignore elif os.environ['QT_API'] == 'pyqt': from PyQt4.QtWebKit import (QWebPage, QWebView, # analysis:ignore QWebSettings) else: from PySide.QtWebKit import * # analysis:ignore guidata-1.7.4/guidata/qtwidgets.py0000666000000000000000000001051712601727200015647 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2011 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ qtwidgets --------- The ``guidata.qtwidgets`` module provides ready-to-use or generic widgets for developing easily Qt-based graphical user interfaces. """ from math import cos, sin, pi from guidata.qt import PYQT5 from guidata.qt.QtGui import QLabel, QPainter, QPen, QWidget, QDockWidget from guidata.qt.QtCore import QSize, Qt # Local imports: from guidata.configtools import get_family class RotatedLabel(QLabel): """ Rotated QLabel (rich text is not supported) Arguments: * parent: parent widget * angle=270 (int): rotation angle in degrees * family (string): font family * bold (bool): font weight * italic (bool): font italic style * color (QColor): font color """ def __init__(self, text, parent=None, angle=270, family=None, bold=False, italic=False, color=None): QLabel.__init__(self, text, parent) font = self.font() if family is not None: font.setFamily(get_family(family)) font.setBold(bold) font.setItalic(italic) self.setFont(font) self.color = color self.angle = angle self.setAlignment(Qt.AlignCenter) def paintEvent(self, evt): painter = QPainter(self) if self.color is not None: painter.setPen(QPen(self.color)) painter.rotate(self.angle) transform = painter.transform().inverted()[0] rct = transform.mapRect(self.rect()) painter.drawText(rct, self.alignment(), self.text()) def sizeHint(self): hint = QLabel.sizeHint(self) width, height = hint.width(), hint.height() angle = self.angle*pi/180 rotated_width = abs(width*cos(angle))+abs(height*sin(angle)) rotated_height = abs(width*sin(angle))+abs(height*cos(angle)) return QSize(rotated_width, rotated_height) def minimumSizeHint(self): return self.sizeHint() class DockableWidgetMixin(object): ALLOWED_AREAS = Qt.AllDockWidgetAreas LOCATION = Qt.TopDockWidgetArea FEATURES = QDockWidget.DockWidgetClosable | \ QDockWidget.DockWidgetFloatable | \ QDockWidget.DockWidgetMovable def __init__(self, parent): self.parent_widget = parent self._isvisible = False self.dockwidget = None self._allowed_areas = self.ALLOWED_AREAS self._location = self.LOCATION self._features = self.FEATURES def setup_dockwidget(self, location=None, features=None, allowed_areas=None): assert self.dockwidget is None, "Dockwidget must be setup before calling 'create_dockwidget'" if location is not None: self._location = location if features is not None: self._features = features if allowed_areas is not None: self._allowed_areas = allowed_areas def get_focus_widget(self): pass def create_dockwidget(self, title): """Add to parent QMainWindow as a dock widget""" dock = QDockWidget(title, self.parent_widget) dock.setObjectName(self.__class__.__name__+"_dw") dock.setAllowedAreas(self._allowed_areas) dock.setFeatures(self._features) dock.setWidget(self) dock.visibilityChanged.connect(self.visibility_changed) self.dockwidget = dock return (dock, self._location) def is_visible(self): return self._isvisible def visibility_changed(self, enable): """DockWidget visibility has changed""" if enable: self.dockwidget.raise_() widget = self.get_focus_widget() if widget is not None: widget.setFocus() self._isvisible = enable and self.dockwidget.isVisible() class DockableWidget(QWidget, DockableWidgetMixin): def __init__(self, parent): if PYQT5: super(DockableWidget, self).__init__(parent, parent=parent) else: QWidget.__init__(self, parent) DockableWidgetMixin.__init__(self, parent) guidata-1.7.4/guidata/qthelpers.py0000666000000000000000000001456412606024534015656 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ qthelpers --------- The ``guidata.qthelpers`` module provides helper functions for developing easily Qt-based graphical user interfaces. """ from __future__ import print_function import sys from guidata.qt.QtGui import (QAction, QApplication, QColor, QHBoxLayout, QIcon, QKeySequence, QLabel, QLineEdit, QMenu, QPushButton, QStyle, QToolButton, QVBoxLayout, QWidget, QGroupBox) from guidata.qt.QtCore import Qt # Local imports: from guidata.configtools import get_icon from guidata.py3compat import is_text_string def text_to_qcolor(text): """Create a QColor from specified string""" color = QColor() if not is_text_string(text): # testing for QString (PyQt API#1) text = str(text) if text.startswith('#') and len(text)==7: correct = '#0123456789abcdef' for char in text: if char.lower() not in correct: return color elif text not in list(QColor.colorNames()): return color color.setNamedColor(text) return color def create_action(parent, title, triggered=None, toggled=None, shortcut=None, icon=None, tip=None, checkable=None, context=Qt.WindowShortcut, enabled=None): """ Create a new QAction """ action = QAction(title, parent) if triggered: if checkable: action.triggered.connect(triggered) else: action.triggered.connect(lambda checked=False: triggered()) if checkable is not None: # Action may be checkable even if the toggled signal is not connected action.setCheckable(checkable) if toggled: action.toggled.connect(toggled) action.setCheckable(True) if icon is not None: assert isinstance(icon, QIcon) action.setIcon( icon ) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if enabled is not None: action.setEnabled(enabled) action.setShortcutContext(context) return action def create_toolbutton(parent, icon=None, text=None, triggered=None, tip=None, toggled=None, shortcut=None, autoraise=True, enabled=None): """Create a QToolButton""" if autoraise: button = QToolButton(parent) else: button = QPushButton(parent) if text is not None: button.setText(text) if icon is not None: if is_text_string(icon): icon = get_icon(icon) button.setIcon(icon) if text is not None or tip is not None: button.setToolTip(text if tip is None else tip) if autoraise: button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) button.setAutoRaise(True) if triggered is not None: button.clicked.connect(lambda checked=False: triggered()) if toggled is not None: button.toggled.connect(toggled) button.setCheckable(True) if shortcut is not None: button.setShortcut(shortcut) if enabled is not None: button.setEnabled(enabled) return button def create_groupbox(parent, title=None, toggled=None, checked=None, flat=False, layout=None): """Create a QGroupBox""" if title is None: group = QGroupBox(parent) else: group = QGroupBox(title, parent) group.setFlat(flat) if toggled is not None: group.setCheckable(True) if checked is not None: group.setChecked(checked) group.toggled.connect(toggled) if layout is not None: group.setLayout(layout) return group def keybinding(attr): """Return keybinding""" ks = getattr(QKeySequence, attr) return QKeySequence.keyBindings(ks)[0].toString() def add_separator(target): """Add separator to target only if last action is not a separator""" target_actions = list(target.actions()) if target_actions: if not target_actions[-1].isSeparator(): target.addSeparator() def add_actions(target, actions): """ Add actions (list of QAction instances) to target (menu, toolbar) """ for action in actions: if isinstance(action, QAction): target.addAction(action) elif isinstance(action, QMenu): target.addMenu(action) elif action is None: add_separator(target) def get_std_icon(name, size=None): """ Get standard platform icon Call 'show_std_icons()' for details """ if not name.startswith('SP_'): name = 'SP_'+name icon = QWidget().style().standardIcon( getattr(QStyle, name) ) if size is None: return icon else: return QIcon( icon.pixmap(size, size) ) class ShowStdIcons(QWidget): """ Dialog showing standard icons """ def __init__(self, parent): QWidget.__init__(self, parent) layout = QHBoxLayout() row_nb = 14 cindex = 0 for child in dir(QStyle): if child.startswith('SP_'): if cindex == 0: col_layout = QVBoxLayout() icon_layout = QHBoxLayout() icon = get_std_icon(child) label = QLabel() label.setPixmap(icon.pixmap(32, 32)) icon_layout.addWidget( label ) icon_layout.addWidget( QLineEdit(child.replace('SP_', '')) ) col_layout.addLayout(icon_layout) cindex = (cindex+1) % row_nb if cindex == 0: layout.addLayout(col_layout) self.setLayout(layout) self.setWindowTitle('Standard Platform Icons') self.setWindowIcon(get_std_icon('TitleBarMenuButton')) def show_std_icons(): """ Show all standard Icons """ app = QApplication(sys.argv) dialog = ShowStdIcons(None) dialog.show() sys.exit(app.exec_()) if __name__ == "__main__": from guidata.utils import pairs print(list( pairs( list(range(5)) ) )) show_std_icons() guidata-1.7.4/guidata/py3compat.py0000666000000000000000000001407512601727200015556 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2012-2013 Pierre Raybaut # Licensed under the terms of the MIT License # (see spyderlib/__init__.py for details) """ guidata.py3compat (exact copy of spyderlib.py3compat) ----------------------------------------------------- Transitional module providing compatibility functions intended to help migrating from Python 2 to Python 3. This module should be fully compatible with: * Python >=v2.6 * Python 3 """ from __future__ import print_function import sys import os PY2 = sys.version[0] == '2' PY3 = sys.version[0] == '3' #============================================================================== # Data types #============================================================================== if PY2: # Python 2 TEXT_TYPES = (str, unicode) INT_TYPES = (int, long) else: # Python 3 TEXT_TYPES = (str,) INT_TYPES = (int,) NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex]) #============================================================================== # Renamed/Reorganized modules #============================================================================== if PY2: # Python 2 import __builtin__ as builtins import ConfigParser as configparser try: import _winreg as winreg except ImportError: pass from sys import maxint as maxsize try: import CStringIO as io except ImportError: import StringIO as io try: import cPickle as pickle except ImportError: import pickle from UserDict import DictMixin as MutableMapping else: # Python 3 import builtins import configparser try: import winreg except ImportError: pass from sys import maxsize import io import pickle from collections import MutableMapping #============================================================================== # Strings #============================================================================== def is_text_string(obj): """Return True if `obj` is a text string, False if it is anything else, like binary data (Python 3) or QString (Python 2, PyQt API #1)""" if PY2: # Python 2 return isinstance(obj, basestring) else: # Python 3 return isinstance(obj, str) def is_binary_string(obj): """Return True if `obj` is a binary string, False if it is anything else""" if PY2: # Python 2 return isinstance(obj, str) else: # Python 3 return isinstance(obj, bytes) def is_string(obj): """Return True if `obj` is a text or binary Python string object, False if it is anything else, like a QString (Python 2, PyQt API #1)""" return is_text_string(obj) or is_binary_string(obj) def is_unicode(obj): """Return True if `obj` is unicode""" if PY2: # Python 2 return isinstance(obj, unicode) else: # Python 3 return isinstance(obj, str) def to_text_string(obj, encoding=None): """Convert `obj` to (unicode) text string""" if PY2: # Python 2 if encoding is None: return unicode(obj) else: return unicode(obj, encoding) else: # Python 3 if encoding is None: return str(obj) elif isinstance(obj, str): # In case this function is not used properly, this could happen return obj else: return str(obj, encoding) def to_binary_string(obj, encoding=None): """Convert `obj` to binary string (bytes in Python 3, str in Python 2)""" if PY2: # Python 2 if encoding is None: return str(obj) else: return obj.encode(encoding) else: # Python 3 return bytes(obj, 'utf-8' if encoding is None else encoding) #============================================================================== # Function attributes #============================================================================== def get_func_code(func): """Return function code object""" if PY2: # Python 2 return func.func_code else: # Python 3 return func.__code__ def get_func_name(func): """Return function name""" if PY2: # Python 2 return func.func_name else: # Python 3 return func.__name__ def get_func_defaults(func): """Return function default argument values""" if PY2: # Python 2 return func.func_defaults else: # Python 3 return func.__defaults__ #============================================================================== # Special method attributes #============================================================================== def get_meth_func(obj): """Return method function object""" if PY2: # Python 2 return obj.im_func else: # Python 3 return obj.__func__ def get_meth_class_inst(obj): """Return method class instance""" if PY2: # Python 2 return obj.im_self else: # Python 3 return obj.__self__ def get_meth_class(obj): """Return method class""" if PY2: # Python 2 return obj.im_class else: # Python 3 return obj.__self__.__class__ #============================================================================== # Misc. #============================================================================== if PY2: # Python 2 input = raw_input getcwd = os.getcwdu cmp = cmp import string str_lower = string.lower else: # Python 3 input = input getcwd = os.getcwd def cmp(a, b): return (a > b) - (a < b) str_lower = str.lower def qbytearray_to_str(qba): """Convert QByteArray object to str in a way compatible with Python 2/3""" return str(bytes(qba.toHex()).decode()) if __name__ == '__main__': pass guidata-1.7.4/guidata/guitest.py0000666000000000000000000001362112601727200015317 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ GUI-based test launcher """ import sys import os import os.path as osp import subprocess from spyderlib.widgets.sourcecode.codeeditor import CodeEditor # Local imports from guidata.qt.QtGui import (QWidget, QVBoxLayout, QSplitter, QFont, QListWidget, QPushButton, QLabel, QGroupBox, QHBoxLayout, QShortcut, QKeySequence) from guidata.qt.QtCore import Qt, QSize from guidata.config import _ from guidata.configtools import get_icon, get_family, MONOSPACE from guidata.qthelpers import get_std_icon def get_tests(test_package): tests = [] test_path = osp.dirname(osp.realpath(test_package.__file__)) for fname in sorted(os.listdir(test_path)): module_name, ext = osp.splitext(fname) if ext not in ('.py', '.pyw'): continue if not module_name.startswith('_'): _temp = __import__(test_package.__name__, fromlist=[module_name]) test_module = getattr(_temp, module_name) test = TestModule(test_module) if test.is_visible(): tests.append(test) return tests class TestModule(object): def __init__(self, test_module): self.module = test_module self.filename = osp.splitext(osp.abspath(test_module.__file__))[0]+'.py' if not osp.isfile(self.filename): self.filename += 'w' def is_visible(self): return hasattr(self.module, 'SHOW') and self.module.SHOW def get_description(self): doc = self.module.__doc__ if doc is None or not doc.strip(): return _("No description available") else: lines = doc.strip().splitlines() format = '%s' lines[0] = format % lines[0] return '
'.join(lines) def run(self, args=''): # Keep the same sys.path environment in child process: # (useful when the program is executed from Spyder, for example) os.environ['PYTHONPATH'] = os.pathsep.join(sys.path) command = [sys.executable, '"'+self.filename+'"'] if args: command.append(args) subprocess.Popen(" ".join(command), shell=True) class TestPropertiesWidget(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) font = QFont(get_family(MONOSPACE), 10, QFont.Normal) info_icon = QLabel() icon = get_std_icon('MessageBoxInformation').pixmap(24, 24) info_icon.setPixmap(icon) info_icon.setFixedWidth(32) info_icon.setAlignment(Qt.AlignTop) self.desc_label = QLabel() self.desc_label.setWordWrap(True) self.desc_label.setAlignment(Qt.AlignTop) self.desc_label.setFont(font) group_desc = QGroupBox(_("Description"), self) layout = QHBoxLayout() layout.addWidget(info_icon) layout.addWidget(self.desc_label) group_desc.setLayout(layout) self.editor = CodeEditor(self) self.editor.setup_editor(linenumbers=True, font=font) self.editor.setReadOnly(True) group_code = QGroupBox(_("Source code"), self) layout = QVBoxLayout() layout.addWidget(self.editor) group_code.setLayout(layout) self.run_button = QPushButton(get_icon("apply.png"), _("Run this script"), self) self.quit_button = QPushButton(get_icon("exit.png"), _("Quit"), self) hlayout = QHBoxLayout() hlayout.addWidget(self.run_button) hlayout.addStretch() hlayout.addWidget(self.quit_button) vlayout = QVBoxLayout() vlayout.addWidget(group_desc) vlayout.addWidget(group_code) vlayout.addLayout(hlayout) self.setLayout(vlayout) def set_item(self, test): self.desc_label.setText(test.get_description()) self.editor.set_text_from_file(test.filename) class TestLauncherWindow(QSplitter): def __init__(self, package, parent=None): QSplitter.__init__(self, parent) self.setWindowTitle(_("Tests - %s module") % package.__name__) self.setWindowIcon(get_icon("%s.svg" % package.__name__, "guidata.svg")) test_package_name = '%s.tests' % package.__name__ _temp = __import__(test_package_name) test_package = sys.modules[test_package_name] tests = get_tests(test_package) listwidget = QListWidget(self) listwidget.addItems([osp.basename(test.filename) for test in tests]) self.properties = TestPropertiesWidget(self) self.addWidget(listwidget) self.addWidget(self.properties) self.properties.run_button.clicked.connect( lambda: tests[listwidget.currentRow()].run()) self.properties.quit_button.clicked.connect(self.close) listwidget.currentRowChanged.connect( lambda row: self.properties.set_item(tests[row])) listwidget.itemActivated.connect( lambda: tests[listwidget.currentRow()].run()) listwidget.setCurrentRow(0) QShortcut(QKeySequence("Escape"), self, self.close) self.setSizes([150, 1]) self.setStretchFactor(1, 1) self.resize(QSize(950, 600)) self.properties.set_item(tests[0]) def run_testlauncher(package): """Run test launcher""" from guidata.qt.QtGui import QApplication app = QApplication([]) win = TestLauncherWindow(package) win.show() app.exec_() guidata-1.7.4/guidata/utils.py0000666000000000000000000002765312601727200015005 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) # pylint: disable=C0103 """ utils ----- The ``guidata.utils`` module provides various utility helper functions (pure python). """ from __future__ import print_function import sys import time import subprocess import os import os.path as osp import locale # Warning: 2to3 false alarm ('import' fixer) import collections from guidata.py3compat import (is_unicode, to_text_string, is_text_string, get_func_code, get_func_name) #============================================================================== # Misc. #============================================================================== def min_equals_max(min, max): """ Return True if minimium value equals maximum value Return False if not, or if maximum or minimum value is not defined """ return min is not None and max is not None and min == max def pairs(iterable): """A simple generator that takes a list and generates pairs [ (l[0],l[1]), ..., (l[n-2], l[n-1])] """ iterator = iter(iterable) first = next(iterator) while True: second = next(iterator) yield (first, second) first = second def add_extension(item, value): """Add extension to filename `item`: data item representing a file path `value`: possible value for data item""" value = to_text_string(value) formats = item.get_prop("data", "formats") if len(formats) == 1 and formats[0] != '*': if not value.endswith('.' + formats[0]) and len(value) > 0: return value + '.' + formats[0] return value def bind(fct, value): """ Returns a callable representing the function 'fct' with it's first argument bound to the value if g = bind(f,1) and f is a function of x,y,z then g(y,z) will return f(1,y,z) """ def callback(*args, **kwargs): return fct(value, *args, **kwargs) return callback def trace(fct): """A decorator that traces function entry/exit used for debugging only """ from functools import wraps @wraps(fct) def wrapper(*args, **kwargs): """Tracing function entry/exit (debugging only)""" print("enter:", fct.__name__) res = fct(*args, **kwargs) print("leave:", fct.__name__) return res return wrapper #============================================================================== # Strings #============================================================================== def decode_fs_string(string): """Convert string from file system charset to unicode""" charset = sys.getfilesystemencoding() if charset is None: charset = locale.getpreferredencoding() return string.decode(charset) #TODO: Py3/I'm really not satisfied with this code even if it's compatible with Py3 def utf8_to_unicode(string): """Convert UTF-8 string to Unicode""" if not is_text_string(string): string = to_text_string(string) if not is_unicode(string): try: string = to_text_string(string, "utf-8") except UnicodeDecodeError: # This is border line... but we admit here string which has been # erroneously encoded in file system charset instead of UTF-8 string = decode_fs_string(string) return string # Findout the encoding used for stdout or use ascii as default STDOUT_ENCODING = "ascii" if hasattr(sys.stdout, "encoding"): if sys.stdout.encoding: STDOUT_ENCODING = sys.stdout.encoding def unicode_to_stdout(ustr): """convert a unicode string to a byte string encoded for stdout output""" return ustr.encode(STDOUT_ENCODING, "replace") #============================================================================== # Updating, restoring datasets #============================================================================== def update_dataset(dest, source, visible_only=False): """ Update `dest` dataset items from `source` dataset dest should inherit from DataSet, whereas source can be: * any Python object containing matching attribute names * or a dictionary with matching key names For each DataSet item, the function will try to get the attribute of the same name from the source. visible_only: if True, update only visible items """ for item in dest._items: key = item._name if hasattr(source, key): try: hide = item.get_prop_value("display", source, "hide", False) except AttributeError: #FIXME: Remove this try...except hide = False if visible_only and hide: continue setattr(dest, key, getattr(source, key)) elif isinstance(source, dict) and key in source: setattr(dest, key, source[key]) def restore_dataset(source, dest): """ Restore `dest` dataset items from `source` dataset This function is almost the same as update_dataset but requires the source to be a DataSet instead of the destination. Symetrically from update_dataset, `dest` may also be a dictionary. """ for item in source._items: key = item._name value = getattr(source, key) if hasattr(dest, key): try: setattr(dest, key, value) except AttributeError: # This attribute is a property, skipping this iteration continue elif isinstance(dest, dict): dest[key] = value #============================================================================== # Interface checking #============================================================================== def assert_interface_supported(klass, iface): """Makes sure a class supports an interface""" for name, func in list(iface.__dict__.items()): if name == '__inherits__': continue if isinstance(func, collections.Callable): assert hasattr(klass, name), \ "Attribute %s missing from %r" % (name, klass) imp_func = getattr(klass, name) imp_code = get_func_code(imp_func) code = get_func_code(func) imp_nargs = imp_code.co_argcount nargs = code.co_argcount if imp_code.co_varnames[:imp_nargs] != code.co_varnames[:nargs]: assert False, "Arguments of %s.%s differ from interface: "\ "%r!=%r" % ( klass.__name__, get_func_name(imp_func), imp_code.co_varnames[:imp_nargs], code.co_varnames[:nargs] ) else: pass # should check class attributes for consistency def assert_interfaces_valid(klass): """Makes sure a class supports the interfaces it declares""" assert hasattr(klass, "__implements__"), \ "Class doesn't implements anything" for iface in klass.__implements__: assert_interface_supported(klass, iface) if hasattr(iface, "__inherits__"): base = iface.__inherits__() assert issubclass(klass, base), \ "%s should be a subclass of %s" % (klass, base) #============================================================================== # Date, time, timer #============================================================================== def localtime_to_isodate(time_struct): """Convert local time to ISO date""" s = time.strftime("%Y-%m-%d %H:%M:%S ", time_struct) s += "%+05d" % time.timezone return s def isodate_to_localtime(datestr): """Convert ISO date to local time""" return time.strptime(datestr[:16], "%Y-%m-%d %H:%M:%S") class FormatTime(object): """Helper object that substitute as a string to format seconds into (nn H mm min ss s)""" def __init__(self, hours_fmt="%d H ", min_fmt="%d min ", sec_fmt="%d s"): self.sec_fmt = sec_fmt self.hours_fmt = hours_fmt self.min_fmt = min_fmt def __mod__(self, val): val = val[0] hours = val // 3600. minutes = (val % 3600.) // 60 seconds = (val % 60.) if hours: return ((self.hours_fmt % hours) + (self.min_fmt % minutes) + (self.sec_fmt % seconds)) elif minutes: return ((self.min_fmt % minutes) + (self.sec_fmt % seconds)) else: return (self.sec_fmt % seconds) format_hms = FormatTime() class Timer(object): """MATLAB-like timer: tic, toc""" def __init__(self): self.t0_dict = {} def tic(self, cat): """Starting timer""" print(">", cat) self.t0_dict[cat] = time.clock() def toc(self, cat, msg="delta:"): """Stopping timer""" print("<", cat, ":", msg, time.clock() - self.t0_dict[cat]) _TIMER = Timer() tic = _TIMER.tic toc = _TIMER.toc #============================================================================== # Module, scripts, programs #============================================================================== def get_module_path(modname): """Return module *modname* base path""" module = sys.modules.get(modname, __import__(modname)) return osp.abspath(osp.dirname(module.__file__)) def is_program_installed(basename): """Return program absolute path if installed in PATH Otherwise, return None""" for path in os.environ["PATH"].split(os.pathsep): abspath = osp.join(path, basename) if osp.isfile(abspath): return abspath def run_program(name, args='', cwd=None, shell=True, wait=False): """Run program in a separate process""" path = is_program_installed(name) if not path: raise RuntimeError("Program %s was not found" % name) command = [path] if args: command.append(args) if wait: subprocess.call(" ".join(command), cwd=cwd, shell=shell) else: subprocess.Popen(" ".join(command), cwd=cwd, shell=shell) def is_module_available(module_name): """Return True if Python module is available""" try: __import__(module_name) return True except ImportError: return False #============================================================================== # Utilities for setup.py scripts #============================================================================== def get_package_data(name, extlist, exclude_dirs=[]): """ Return data files for package *name* with extensions in *extlist* (search recursively in package directories) """ assert isinstance(extlist, (list, tuple)) flist = [] # Workaround to replace os.path.relpath (not available until Python 2.6): offset = len(name)+len(os.pathsep) for dirpath, _dirnames, filenames in os.walk(name): if dirpath not in exclude_dirs: for fname in filenames: if osp.splitext(fname)[1].lower() in extlist: flist.append(osp.join(dirpath, fname)[offset:]) return flist def get_subpackages(name): """Return subpackages of package *name*""" splist = [] for dirpath, _dirnames, _filenames in os.walk(name): if osp.isfile(osp.join(dirpath, '__init__.py')): splist.append(".".join(dirpath.split(os.sep))) return splist def cythonize_all(relpath): """Cythonize all Cython modules in relative path""" from Cython.Compiler import Main for fname in os.listdir(relpath): if osp.splitext(fname)[1] == '.pyx': Main.compile(osp.join(relpath, fname)) guidata-1.7.4/guidata/gettext_helpers.py0000666000000000000000000000774112601727200017047 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) from __future__ import print_function import sys import os import os.path as osp import subprocess if os.name == 'nt': # Find pygettext.py source on a windows install pygettext = ['python', osp.join(sys.prefix, "Tools", "i18n", "pygettext.py")] msgfmt = ['python', osp.join(sys.prefix, "Tools", "i18n", "msgfmt.py")] else: pygettext = ['pygettext'] msgfmt = ['msgfmt'] def get_files(modname): if not osp.isdir(modname): return [modname] files = [] for dirname, _dirnames, filenames in os.walk(modname): files += [ osp.join(dirname, f) for f in filenames if f.endswith(".py") or f.endswith(".pyw") ] for dirname, _dirnames, filenames in os.walk("tests"): files += [ osp.join(dirname, f) for f in filenames if f.endswith(".py") or f.endswith(".pyw") ] return files def get_lang( modname ): localedir = osp.join( modname, "locale") for _dirname, dirnames, _filenames in os.walk(localedir): break # we just want the list of first level directories return dirnames def do_rescan(modname): files = get_files(modname) dirname = modname do_rescan_files(files, modname, dirname) def do_rescan_files(files, modname, dirname): localedir = osp.join(dirname, "locale") potfile = modname+".pot" subprocess.call(pygettext+[ ##"-D", # Extract docstrings "-o", potfile, # Nom du fichier pot "-p", localedir, # dest ]+files) for lang in get_lang(dirname): pofilepath = osp.join(localedir, lang, "LC_MESSAGES", modname+".po") potfilepath = osp.join(localedir, potfile) print("Updating...", pofilepath) if not osp.exists( osp.join(localedir, lang, "LC_MESSAGES") ): os.mkdir( osp.join(localedir, lang, "LC_MESSAGES") ) if not osp.exists( pofilepath ): outf = open(pofilepath, "w") outf.write("# -*- coding: utf-8 -*-\n") data = open( potfilepath ).read() data = data.replace("charset=CHARSET", "charset=utf-8") data = data.replace("Content-Transfer-Encoding: ENCODING", "Content-Transfer-Encoding: utf-8") outf.write(data) else: print("merge...") subprocess.call( ["msgmerge", "-o", pofilepath, pofilepath, potfilepath] ) def do_compile(modname, dirname=None): if dirname is None: dirname = modname localedir = osp.join(dirname, "locale") for lang in get_lang(dirname): pofilepath = osp.join(localedir, lang, "LC_MESSAGES", modname+".po") subprocess.call( msgfmt+[pofilepath] ) def main( modname ): if len(sys.argv)<2: cmd = "help" else: cmd = sys.argv[1] # lang = get_lang( modname ) if cmd=="help": print("Available commands:") print(" help : this message") print(" help_gettext : pygettext --help") print(" help_msgfmt : msgfmt --help") print(" scan : rescan .py files and updates existing .po files") print(" compile : recompile .po files") print() print("Pour fonctionner ce programme doit être lancé depuis") print("la racine du module") print("Traductions disponibles:") for i in get_lang(modname): print(i) elif cmd=="help_gettext": subprocess.call( pygettext+["--help"] ) elif cmd=="help_msgfmt": subprocess.call( msgfmt+["--help"] ) elif cmd=="scan": print("Updating pot files") do_rescan( modname ) elif cmd=="compile": print("Builtin .mo files") do_compile( modname ) guidata-1.7.4/guidata/tests/0000755000000000000000000000000012632514315014421 5ustar rootrootguidata-1.7.4/guidata/tests/activable_items.py0000666000000000000000000000212612601727200020126 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2012 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ Example with activable items: items which active state is changed depending on another item's value. """ SHOW = True # Show test in GUI-based test launcher from guidata.dataset.dataitems import ChoiceItem, FloatItem from guidata.dataset.datatypes import DataSet, GetAttrProp, FuncProp choices = (('A', 'Choice #1: A'), ('B', 'Choice #2: B'), ('C', 'Choice #3: C')) class Test(DataSet): _prop = GetAttrProp("choice") choice = ChoiceItem('Choice', choices).set_prop("display", store=_prop) x1 = FloatItem('x1') x2 = FloatItem('x2').set_prop("display", active=FuncProp(_prop, lambda x: x=='B')) x3 = FloatItem('x3').set_prop("display", active=FuncProp(_prop, lambda x: x=='C')) if __name__ == '__main__': # Create QApplication import guidata _app = guidata.qapplication() test = Test() test.edit() guidata-1.7.4/guidata/tests/inheritance.py0000666000000000000000000000216112601727200017263 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ DataSet objects inheritance test From time to time, it may be useful to derive a DataSet from another. The main application is to extend a parameter set with additionnal parameters. """ from __future__ import print_function SHOW = True # Show test in GUI-based test launcher from guidata.tests.all_features import TestParameters from guidata.dataset.datatypes import BeginGroup, EndGroup from guidata.dataset.dataitems import FloatItem, BoolItem class TestParameters2(TestParameters): bool1 = BoolItem("Boolean option (bis)") g1 = BeginGroup("Group") a = FloatItem("Level 1") gg1 = BeginGroup("sub-group") b = FloatItem("Level 2a") c = FloatItem("Level 2b") _gg1 = EndGroup("sub-group end") _g1 = EndGroup("sub-group") if __name__ == '__main__': # Create QApplication import guidata _app = guidata.qapplication() e = TestParameters2() e.edit() print(e) guidata-1.7.4/guidata/tests/all_items.py0000666000000000000000000000764312601727200016755 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ All guidata DataItem objects demo A DataSet object is a set of parameters of various types (integer, float, boolean, string, etc.) which may be edited in a dialog box thanks to the 'edit' method. Parameters are defined by assigning DataItem objects to a DataSet class definition: each parameter type has its own DataItem class (IntItem for integers, FloatItem for floats, StringItem for strings, etc.) """ from __future__ import print_function SHOW = True # Show test in GUI-based test launcher import tempfile, atexit, shutil, datetime, numpy as np from guidata.dataset.datatypes import DataSet, BeginGroup, EndGroup from guidata.dataset.dataitems import (FloatItem, IntItem, BoolItem, ChoiceItem, MultipleChoiceItem, ImageChoiceItem, FilesOpenItem, StringItem, TextItem, ColorItem, FileSaveItem, FileOpenItem, DirectoryItem, FloatArrayItem, DateItem, DateTimeItem) # Creating temporary files and registering cleanup functions TEMPDIR = tempfile.mkdtemp(prefix="test_") atexit.register(shutil.rmtree, TEMPDIR) FILE_ETA = tempfile.NamedTemporaryFile(suffix=".eta", dir=TEMPDIR) atexit.register(FILE_ETA.close) FILE_CSV = tempfile.NamedTemporaryFile(suffix=".csv", dir=TEMPDIR) atexit.register(FILE_CSV.close) class TestParameters(DataSet): """ DataSet test The following text is the DataSet 'comment':
Plain text or rich text2 are both supported, as well as special characters (α, β, γ, δ, ...) """ dir = DirectoryItem("Directory", TEMPDIR) fname = FileOpenItem("Open file", ("csv", "eta"), FILE_CSV.name) fnames = FilesOpenItem("Open files", "csv", FILE_CSV.name) fname_s = FileSaveItem("Save file", "eta", FILE_ETA.name) string = StringItem("String") text = TextItem("Text") float_slider = FloatItem("Float (with slider)", default=0.5, min=0, max=1, step=0.01, slider=True) integer = IntItem("Integer", default=5, min=3, max=16, slider=True ).set_pos(col=1) dtime = DateTimeItem("Date/time", default=datetime.datetime(2010, 10, 10)) date = DateItem("Date", default=datetime.date(2010, 10, 10)).set_pos(col=1) bool1 = BoolItem("Boolean option without label") bool2 = BoolItem("Boolean option with label", "Label") _bg = BeginGroup("A sub group") color = ColorItem("Color", default="red") choice = ChoiceItem("Single choice 1", [('16', "first choice"), ('32', "second choice"), ('64', "third choice")]) mchoice2 = ImageChoiceItem("Single choice 2", [("rect", "first choice", "gif.png" ), ("ell", "second choice", "txt.png" ), ("qcq", "third choice", "file.png" )] ) _eg = EndGroup("A sub group") floatarray = FloatArrayItem("Float array", default=np.ones( (50, 5), float), format=" %.2e ").set_pos(col=1) mchoice3 = MultipleChoiceItem("MC type 1", [ str(i) for i in range(12)] ).horizontal(4) mchoice1 = MultipleChoiceItem("MC type 2", ["first choice", "second choice", "third choice"]).vertical(1).set_pos(col=1) if __name__ == "__main__": # Create QApplication import guidata _app = guidata.qapplication() e = TestParameters() e.floatarray[:, 0] = np.linspace( -5, 5, 50) print(e) if e.edit(): print(e) e.view()guidata-1.7.4/guidata/tests/__init__.py0000666000000000000000000000063612601727200016536 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ guidata test package ==================== """ def run(): """Run guidata test launcher""" import guidata from guidata.guitest import run_testlauncher run_testlauncher(guidata) if __name__ == '__main__': run()guidata-1.7.4/guidata/tests/datasetgroup.py0000666000000000000000000000150412601727200017474 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ DataSetGroup demo DataSet objects may be grouped into DataSetGroup, allowing them to be edited in a single dialog box (with one tab per DataSet object). """ from __future__ import print_function SHOW = True # Show test in GUI-based test launcher from guidata.tests.all_features import TestParameters from guidata.dataset.datatypes import DataSetGroup if __name__ == "__main__": # Create QApplication import guidata _app = guidata.qapplication() e1 = TestParameters("DataSet #1") e2 = TestParameters("DataSet #2") g = DataSetGroup( [e1, e2], title='Parameters group' ) g.edit() print(e1) g.edit() guidata-1.7.4/guidata/tests/all_features.py0000666000000000000000000001143112611640164017444 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ All guidata item/group features demo """ from __future__ import print_function SHOW = True # Show test in GUI-based test launcher import tempfile, atexit, shutil import numpy as np from guidata.dataset.datatypes import (DataSet, BeginTabGroup, EndTabGroup, BeginGroup, EndGroup, ObjectItem) from guidata.dataset.dataitems import (FloatItem, IntItem, BoolItem, ChoiceItem, MultipleChoiceItem, ImageChoiceItem, FilesOpenItem, StringItem, TextItem, ColorItem, FileSaveItem, FileOpenItem, DirectoryItem, FloatArrayItem) from guidata.dataset.qtwidgets import DataSetEditLayout, DataSetShowLayout from guidata.dataset.qtitemwidgets import DataSetWidget # Creating temporary files and registering cleanup functions TEMPDIR = tempfile.mkdtemp(prefix="test_") atexit.register(shutil.rmtree, TEMPDIR) FILE_ETA = tempfile.NamedTemporaryFile(suffix=".eta", dir=TEMPDIR) atexit.register(FILE_ETA.close) FILE_CSV = tempfile.NamedTemporaryFile(suffix=".csv", dir=TEMPDIR) atexit.register(FILE_CSV.close) class SubDataSet(DataSet): dir = DirectoryItem("Directory", TEMPDIR) fname = FileOpenItem("Single file (open)", ("csv", "eta"), FILE_CSV.name) fnames = FilesOpenItem("Multiple files", "csv", FILE_CSV.name) fname_s = FileSaveItem("Single file (save)", "eta", FILE_ETA.name) class SubDataSetWidget(DataSetWidget): klass = SubDataSet class SubDataSetItem(ObjectItem): klass = SubDataSet DataSetEditLayout.register(SubDataSetItem, SubDataSetWidget) DataSetShowLayout.register(SubDataSetItem, SubDataSetWidget) class TestParameters(DataSet): """ DataSet test The following text is the DataSet 'comment':
Plain text or rich text2 are both supported, as well as special characters (α, β, γ, δ, ...) """ files = SubDataSetItem("files") string = StringItem("String") text = TextItem("Text") _bg = BeginGroup("A sub group") float_slider = FloatItem("Float (with slider)", default=0.5, min=0, max=1, step=0.01, slider=True) fl1 = FloatItem("Current", default=10., min=1, max=30, unit="mA", help="Threshold current") fl2 = FloatItem("Float (col=1)", default=1., min=1, max=1, help="Help on float item").set_pos(col=1) fl3 = FloatItem("Not checked float").set_prop('data', check_value=False) bool1 = BoolItem("Boolean option without label") bool2 = BoolItem("Boolean option with label", "Label").set_pos(col=1, colspan=2) color = ColorItem("Color", default="red") choice1 = ChoiceItem("Single choice (radio)", [(16, "first choice"), (32, "second choice"), (64, "third choice")], radio=True ).set_pos(col=1, colspan=2) choice2 = ChoiceItem("Single choice (combo)", [(16, "first choice"), (32, "second choice"), (64, "third choice")] ).set_pos(col=1, colspan=2) _eg = EndGroup("A sub group") floatarray = FloatArrayItem("Float array", default=np.ones( (50, 5), float), format=" %.2e ").set_pos(col=1) g0 = BeginTabGroup("group") mchoice1 = MultipleChoiceItem("MC type 1", ["first choice", "second choice", "third choice"]).vertical(2) mchoice2 = ImageChoiceItem("MC type 2", [("rect", "first choice", "gif.png" ), ("ell", "second choice", "txt.png" ), ("qcq", "third choice", "file.png" )] ).set_pos(col=1) \ .set_prop("display", icon="file.png") mchoice3 = MultipleChoiceItem("MC type 3", [ str(i) for i in range(10)] ).horizontal(2) eg0 = EndTabGroup("group") integer_slider = IntItem("Integer (with slider)", default=5, min=-50, max=100, slider=True) integer = IntItem("Integer", default=5, min=3, max=6).set_pos(col=1) if __name__ == "__main__": # Create QApplication import guidata _app = guidata.qapplication() e = TestParameters() e.floatarray[:, 0] = np.linspace( -5, 5, 50) print(e) if e.edit(): e.edit() print(e) e.view()guidata-1.7.4/guidata/tests/editgroupbox.py0000666000000000000000000001340712601727200017512 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ DataSetEditGroupBox and DataSetShowGroupBox demo These group box widgets are intended to be integrated in a GUI application layout, showing read-only parameter sets or allowing to edit parameter values. """ SHOW = True # Show test in GUI-based test launcher from guidata.qt.QtGui import QMainWindow, QSplitter from guidata.dataset.datatypes import (DataSet, BeginGroup, EndGroup, BeginTabGroup, EndTabGroup) from guidata.dataset.dataitems import (ChoiceItem, FloatItem, StringItem, DirectoryItem, FileOpenItem) from guidata.dataset.qtwidgets import DataSetShowGroupBox, DataSetEditGroupBox from guidata.configtools import get_icon from guidata.qthelpers import create_action, add_actions, get_std_icon # Local test import: from guidata.tests.activable_dataset import ExampleDataSet class AnotherDataSet(DataSet): """ Example 2 Simple dataset example """ param0 = ChoiceItem("Choice", ['deazdazk', 'aeazee', '87575757']) param1 = FloatItem("Foobar 1", default=0, min=0) a_group = BeginGroup("A group") param2 = FloatItem("Foobar 2", default=.93) param3 = FloatItem("Foobar 3", default=123) _a_group = EndGroup("A group") class ExampleMultiGroupDataSet(DataSet): param0 = ChoiceItem("Choice", ['deazdazk', 'aeazee', '87575757']) param1 = FloatItem("Foobar 1", default=0, min=0) t_group = BeginTabGroup("T group") a_group = BeginGroup("A group") param2 = FloatItem("Foobar 2", default=.93) dir1 = DirectoryItem("Directory 1") file1 = FileOpenItem("File 1") _a_group = EndGroup("A group") b_group = BeginGroup("B group") param3 = FloatItem("Foobar 3", default=123) _b_group = EndGroup("B group") c_group = BeginGroup("C group") param4 = FloatItem("Foobar 4", default=250) _c_group = EndGroup("C group") _t_group = EndTabGroup("T group") class OtherDataSet(DataSet): title = StringItem("Title", default="Title") icon = ChoiceItem("Icon", (("python.png", "Python"), ("guidata.svg", "guidata"), ("settings.png", "Settings"))) opacity = FloatItem("Opacity", default=1., min=.1, max=1) class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setWindowIcon(get_icon('python.png')) self.setWindowTitle("Application example") # Instantiate dataset-related widgets: self.groupbox1 = DataSetShowGroupBox("Activable dataset", ExampleDataSet, comment='') self.groupbox2 = DataSetShowGroupBox("Standard dataset", AnotherDataSet, comment='') self.groupbox3 = DataSetEditGroupBox("Standard dataset", OtherDataSet, comment='') self.groupbox4 = DataSetEditGroupBox("Standard dataset", ExampleMultiGroupDataSet, comment='') self.groupbox3.SIG_APPLY_BUTTON_CLICKED.connect(self.update_window) self.update_groupboxes() splitter = QSplitter(self) splitter.addWidget(self.groupbox1) splitter.addWidget(self.groupbox2) splitter.addWidget(self.groupbox3) splitter.addWidget(self.groupbox4) self.setCentralWidget(splitter) self.setContentsMargins(10, 5, 10, 5) # File menu file_menu = self.menuBar().addMenu("File") quit_action = create_action(self, "Quit", shortcut="Ctrl+Q", icon=get_std_icon("DialogCloseButton"), tip="Quit application", triggered=self.close) add_actions(file_menu, (quit_action, )) # Edit menu edit_menu = self.menuBar().addMenu("Edit") editparam1_action = create_action(self, "Edit dataset 1", triggered=self.edit_dataset1) editparam2_action = create_action(self, "Edit dataset 2", triggered=self.edit_dataset2) editparam4_action = create_action(self, "Edit dataset 4", triggered=self.edit_dataset4) add_actions(edit_menu, (editparam1_action, editparam2_action, editparam4_action)) def update_window(self): dataset = self.groupbox3.dataset self.setWindowTitle(dataset.title) self.setWindowIcon(get_icon(dataset.icon)) self.setWindowOpacity(dataset.opacity) def update_groupboxes(self): self.groupbox1.dataset.set_readonly() # This is an activable dataset self.groupbox1.get() self.groupbox2.get() self.groupbox4.get() def edit_dataset1(self): self.groupbox1.dataset.set_writeable() # This is an activable dataset if self.groupbox1.dataset.edit(): self.update_groupboxes() def edit_dataset2(self): if self.groupbox2.dataset.edit(): self.update_groupboxes() def edit_dataset4(self): if self.groupbox4.dataset.edit(): self.update_groupboxes() if __name__ == '__main__': from guidata.qt.QtGui import QApplication import sys app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())guidata-1.7.4/guidata/tests/text.py0000666000000000000000000000127712601727200015765 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """Test in text mode""" from __future__ import print_function SHOW = False # Do not show test in GUI-based test launcher from guidata.dataset.datatypes import DataSet from guidata.dataset.dataitems import FloatItem, IntItem class Parameters(DataSet): height = FloatItem("Height", min=1, max=250, help="height in cm") width = FloatItem("Width", min=1, max=250, help="width in cm") number = IntItem("Number", min=3, max=20) if __name__ == '__main__': p = Parameters() p.text_edit() print(p)guidata-1.7.4/guidata/tests/bool_selector.py0000666000000000000000000000372512601727200017634 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ DataItem groups and group selection DataSet items may be included in groups (these items are then shown in group box widgets in the editing dialog box) and item groups may be enabled/disabled using one group parameter (a boolean item). """ from __future__ import print_function SHOW = True # Show test in GUI-based test launcher from guidata.dataset.datatypes import DataSet, BeginGroup, EndGroup, ValueProp from guidata.dataset.dataitems import BoolItem, FloatItem prop1 = ValueProp(False) prop2 = ValueProp(False) class GroupSelection(DataSet): """ Group selection test Group selection example: """ g1 = BeginGroup("group 1") enable1 = BoolItem("Enable parameter set #1", help="If disabled, the following parameters will be ignored", default=False).set_prop("display", store=prop1) param1_1 = FloatItem("Param 1.1", default=0, min=0).set_prop("display", active=prop1) param1_2 = FloatItem("Param 1.2", default=.93).set_prop("display", active=prop1) _g1 = EndGroup("group 1") g2 = BeginGroup("group 2") enable2 = BoolItem("Enable parameter set #2", help="If disabled, the following parameters will be ignored", default=True).set_prop("display", store=prop2) param2_1 = FloatItem("Param 2.1", default=0, min=0).set_prop("display", active=prop2) param2_2 = FloatItem("Param 2.2", default=.93).set_prop("display", active=prop2) _g2 = EndGroup("group 2") if __name__ == '__main__': # Create QApplication import guidata _app = guidata.qapplication() prm = GroupSelection() prm.edit() print(prm) guidata-1.7.4/guidata/tests/userconfig_app.py0000666000000000000000000000104412611707626020010 0ustar rootroot# -*- coding: utf-8 -*- """ userconfig Application settings example This should create a file named ".app.ini" in your HOME directory containing: [main] version = 1.0.0 [a] b/f = 1.0 """ from guidata.dataset import datatypes as gdt from guidata.dataset import dataitems as gdi from guidata import userconfig class DS(gdt.DataSet): f = gdi.FloatItem('F', 1.0) ds = DS('') uc = userconfig.UserConfig({}) uc.set_application('app','1.0.0') ds.write_config(uc,'a','b') print("Settings saved in: ", uc.filename()) guidata-1.7.4/guidata/tests/activable_dataset.py0000666000000000000000000000305712601727200020436 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ ActivableDataSet example Warning: ActivableDataSet objects were made to be integrated inside GUI layouts. So this example with dialog boxes may be confusing. --> see tests/editgroupbox.py to understand the activable dataset usage """ #When editing, all items are shown. #When showing dataset in read-only mode (e.g. inside another layout), all items #are shown except the enable item. SHOW = True # Show test in GUI-based test launcher from guidata.dataset.datatypes import ActivableDataSet from guidata.dataset.dataitems import BoolItem, FloatItem, ChoiceItem, ColorItem class ExampleDataSet(ActivableDataSet): """ Example Activable dataset example """ enable = BoolItem("Enable parameter set", help="If disabled, the following parameters will be ignored", default=False) param0 = ChoiceItem("Param 0", ['choice #1', 'choice #2', 'choice #3']) param1 = FloatItem("Param 1", default=0, min=0) param2 = FloatItem("Param 2", default=.93) color = ColorItem("Color", default="red") ExampleDataSet.active_setup() if __name__ == '__main__': # Create QApplication import guidata _app = guidata.qapplication() # Editing mode: prm = ExampleDataSet() prm.set_writeable() prm.edit() # Showing mode: prm.set_readonly() prm.view()guidata-1.7.4/guidata/tests/hdf5.py0000666000000000000000000000257512601727200015631 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ HDF5 I/O demo DataSet objects may be saved in HDF5 files, a universal hierarchical dataset file type. This script shows how to save in and then reload data from a HDF5 file. """ try: import guidata.hdf5io #@UnusedImport hdf5_is_available = True except ImportError: hdf5_is_available = False SHOW = hdf5_is_available # Show test in GUI-based test launcher import os from guidata.hdf5io import HDF5Reader, HDF5Writer from guidata.tests.all_items import TestParameters from guidata.dataset.dataitems import StringItem class TestParameters_Light(TestParameters): date = StringItem("D1", default="Replacement for unsupported DateItem") dtime = StringItem("D2", default="Replacement for unsupported DateTimeItem") if __name__ == '__main__': # Create QApplication import guidata _app = guidata.qapplication() if os.path.exists("test.h5"): os.unlink("test.h5") e = TestParameters() if e.edit(): writer = HDF5Writer("test.h5") e.serialize(writer) writer.close() e = TestParameters() reader = HDF5Reader("test.h5") e.deserialize(reader) reader.close() e.edit() guidata-1.7.4/guidata/tests/translations.py0000666000000000000000000000073212601727200017515 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2012 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """Little translation test""" from __future__ import print_function SHOW = False # Do not show test in GUI-based test launcher from guidata.config import _ translations = (_("Some required entries are incorrect"),) if __name__ == '__main__': for text in translations: print(text) guidata-1.7.4/guidata/tests/rotatedlabel.py0000666000000000000000000000233612601727200017440 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ RotatedLabel test RotatedLabel is derived from QLabel: it provides rotated text display. """ from __future__ import unicode_literals SHOW = True # Show test in GUI-based test launcher from guidata.qt.QtGui import QFrame, QGridLayout from guidata.qt.QtCore import Qt from guidata.qtwidgets import RotatedLabel class Frame(QFrame): def __init__(self, parent=None): QFrame.__init__(self, parent) layout = QGridLayout() self.setLayout(layout) angle = 0 for row in range(7): for column in range(7): layout.addWidget(RotatedLabel("Label %03d°" % angle, angle=angle, color=Qt.blue, bold=True), row, column, Qt.AlignCenter) angle += 10 if __name__ == '__main__': from guidata.qt.QtGui import QApplication import sys app = QApplication([]) frame = Frame() frame.show() sys.exit(app.exec_()) guidata-1.7.4/guidata/tests/disthelpers.py0000666000000000000000000000144712617472476017351 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2011 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ guidata.disthelpers demo How to create an executable with py2exe or cx_Freeze with less efforts than writing a complete setup script. """ SHOW = True # Show test in GUI-based test launcher import os.path as osp from guidata.disthelpers import Distribution if __name__ == '__main__': dist = Distribution() dist.setup(name="Application demo", version='1.0.0', description="Application demo based on editgroupbox.py", script=osp.join(osp.dirname(__file__), "editgroupbox.py"), target_name="demo.exe") dist.add_modules('guidata') dist.build('cx_Freeze') guidata-1.7.4/guidata/tests/config.py0000666000000000000000000000301412601727200016235 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """Config test""" SHOW = False # Do not show test in GUI-based test launcher import unittest from guidata.tests.all_features import TestParameters from guidata.config import UserConfig class TestBasic(unittest.TestCase): def setUp(self): self.test_save() def test_save(self): eta = TestParameters() eta.write_config(CONF, "TestParameters", "") #print "fin test_save" def test_load(self): eta = TestParameters() eta.read_config(CONF, "TestParameters", "") #print "fin test_load" def test_default(self): eta = TestParameters() eta.write_config(CONF, "etagere2", "") eta = TestParameters() eta.read_config(CONF, "etagere2", "") self.assertEqual(eta.fl2, 1.) self.assertEqual(eta.integer, 5) #print "fin test_default" def test_restore(self): eta = TestParameters() eta.fl2 = 2 eta.integer = 6 eta.write_config(CONF, "etagere3", "") eta = TestParameters() eta.read_config(CONF, "etagere3", "") self.assertEqual(eta.fl2, 2.) self.assertEqual(eta.integer, 6) #print "fin test_restore" if __name__=="__main__": CONF = UserConfig({}) unittest.main() guidata-1.7.4/guidata/tests/callbacks.py0000666000000000000000000000376012601727200016717 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2011 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ Demonstrates how items may trigger callbacks when activated """ from __future__ import print_function SHOW = True # Show test in GUI-based test launcher from guidata.dataset.datatypes import DataSet from guidata.dataset.dataitems import (ChoiceItem, StringItem, TextItem, ColorItem, FloatItem) class TestParameters(DataSet): def cb_example(self, item, value): print("\nitem: ", item, "\nvalue:", value) if self.results is None: self.results = '' self.results += str(value)+'\n' print("results:", self.results) def update_x1plusx2(self, item, value): print("\nitem: ", item, "\nvalue:", value) if self.x1 is not None and self.x2 is not None: self.x1plusx2 = self.x1 + self.x2 else: self.x1plusx2 = None string = StringItem("String", default="foobar" ).set_prop("display", callback=cb_example) x1 = FloatItem("x1").set_prop("display", callback=update_x1plusx2) x2 = FloatItem("x2").set_prop("display", callback=update_x1plusx2) x1plusx2 = FloatItem("x1+x2").set_prop("display", active=False) color = ColorItem("Color", default="red" ).set_prop("display", callback=cb_example) choice = ChoiceItem("Single choice", [(16, "first choice"), (32, "second choice"), (64, "third choice")], default=64 ).set_pos(col=1, colspan=2 ).set_prop("display", callback=cb_example) results = TextItem("Results") if __name__ == "__main__": # Create QApplication import guidata _app = guidata.qapplication() e = TestParameters() print(e) if e.edit(): print(e) e.view()guidata-1.7.4/guidata/tests/data.py0000666000000000000000000000264212601727200015707 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """Unit tests""" SHOW = False # Do not show test in GUI-based test launcher import unittest from guidata.dataset.datatypes import DataSet from guidata.dataset.dataitems import FloatItem, IntItem from guidata.utils import update_dataset class Parameters(DataSet): float1 = FloatItem("float #1", min=1, max=250, help="height in cm") float2 = FloatItem("float #2", min=1, max=250, help="width in cm") number = IntItem("number", min=3, max=20) class TestCheck(unittest.TestCase): def test_range(self): """Test range checking of FloatItem""" e = Parameters() e.float1 = 150. e.float2 = 400. e.number = 4 errors = e.check() self.assertEquals( errors, ["float2"] ) def test_typechecking(self): """Test type checking of FloatItem""" e = Parameters() e.float1 = 150 e.float2 = 400 e.number = 4. errors = e.check() self.assertEquals( errors, ["float1", "float2", "number"] ) def test_update(self): e1 = Parameters() e2 = Parameters() e1.float1 = 23 update_dataset(e2, e1) self.assertEquals( e2.float1, 23 ) if __name__ == "__main__": unittest.main() guidata-1.7.4/guidata/userconfig.py0000666000000000000000000003331112632321724016002 0ustar rootroot#!/usr/bin/env python # -*- coding: utf-8 -*- # userconfig License Agreement (MIT License) # ------------------------------------------ # # Copyright © 2009-2012 Pierre Raybaut # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without # restriction, including without limitation the rights to use, # copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following # conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. """ userconfig ---------- The ``guidata.userconfig`` module provides user configuration file (.ini file) management features based on ``ConfigParser`` (standard Python library). It is the exact copy of the open-source package `userconfig` (MIT license). """ from __future__ import print_function __version__ = '1.0.7' import os import re import os.path as osp import sys from guidata.py3compat import configparser as cp from guidata.py3compat import is_text_string, is_unicode, PY2 def _check_values(sections): # Checks if all key/value pairs are writable err = False for section, data in list(sections.items()): for key, value in list(data.items()): try: _s = str(value) except Exception as _e: print("Can't convert:") print(section, key, repr(value)) err = True if err: assert False else: import traceback print("-"*30) traceback.print_stack() def get_home_dir(): """ Return user home directory """ try: path = osp.expanduser('~') except: path = '' for env_var in ('HOME', 'USERPROFILE', 'TMP'): if osp.isdir(path): break path = os.environ.get(env_var, '') if path: return path else: raise RuntimeError('Please define environment variable $HOME') def encode_to_utf8(x): """Encode unicode string in UTF-8 -- but only with Python 2""" if PY2 and is_unicode(x): return x.encode("utf-8") else: return x def get_config_dir(): if sys.platform == "win32": # TODO: on windows config files usually go in return get_home_dir() return osp.join(get_home_dir(), ".config") class NoDefault: pass class UserConfig(cp.ConfigParser): """ UserConfig class, based on ConfigParser name: name of the config options: dictionnary containing options *or* list of tuples (section_name, options) Note that 'get' and 'set' arguments number and type differ from the overriden methods """ default_section_name = 'main' def __init__(self, defaults): cp.ConfigParser.__init__(self) self.name = "none" self.raw = 0 # 0=substitutions are enabled / 1=raw config parser assert isinstance(defaults, dict) for _key, val in list(defaults.items()): assert isinstance(val, dict) if self.default_section_name not in defaults: defaults[self.default_section_name] = {} self.defaults = defaults self.reset_to_defaults(save=False) self.check_default_values() def update_defaults(self, defaults): for key, sectdict in list(defaults.items()): if key not in self.defaults: self.defaults[key] = sectdict else: self.defaults[key].update(sectdict) self.reset_to_defaults(save=False) def save(self): # In any case, the resulting config is saved in config file: self.__save() def set_application(self, name, version, load=True, raw_mode=False): self.name = name self.raw = 1 if raw_mode else 0 if (version is not None) and (re.match('^(\d+).(\d+).(\d+)$', version) is None): raise RuntimeError("Version number %r is incorrect - must be in X.Y.Z format" % version) if load: # If config file already exists, it overrides Default options: self.__load() if version != self.get_version(version): # Version has changed -> overwriting .ini file self.reset_to_defaults(save=False) self.__remove_deprecated_options() # Set new version number self.set_version(version, save=False) if self.defaults is None: # If no defaults are defined, set .ini file settings as default self.set_as_defaults() def check_default_values(self): """Check the static options for forbidden data types""" errors = [] def _check(key, value): if value is None: return if isinstance(value, dict): for k, v in list(value.items()): _check(key+"{}", k) _check(key+"/"+k, v) elif isinstance(value, (list, tuple)): for v in value: _check(key+"[]", v) else: if not isinstance(value, (bool, int, float, str)): errors.append("Invalid value for %s: %r" % (key, value)) for name, section in list(self.defaults.items()): assert isinstance(name, str) for key, value in list(section.items()): _check(key, value) if errors: for err in errors: print(err) raise ValueError("Invalid default values") def get_version(self, version='0.0.0'): """Return configuration (not application!) version""" return self.get(self.default_section_name, 'version', version) def set_version(self, version='0.0.0', save=True): """Set configuration (not application!) version""" self.set(self.default_section_name, 'version', version, save=save) def __load(self): """ Load config from the associated .ini file """ try: if PY2: # Python 2 self.read(self.filename()) else: # Python 3 self.read(self.filename(), encoding='utf-8') except cp.MissingSectionHeaderError: print("Warning: File contains no section headers.") def __remove_deprecated_options(self): """ Remove options which are present in the .ini file but not in defaults """ for section in self.sections(): for option, _ in self.items(section, raw=self.raw): if self.get_default(section, option) is NoDefault: self.remove_option(section, option) if len(self.items(section, raw=self.raw)) == 0: self.remove_section(section) def __save(self): """ Save config into the associated .ini file """ fname = self.filename() if osp.isfile(fname): os.remove(fname) if PY2: # Python 2 with open(fname, 'w') as configfile: self.write(configfile) else: # Python 3 with open(fname, 'w', encoding='utf-8') as configfile: self.write(configfile) def filename(self): """ Create a .ini filename located in user home directory """ return osp.join(get_config_dir(), '.%s.ini' % self.name) def cleanup(self): """ Remove .ini file associated to config """ os.remove(self.filename()) def set_as_defaults(self): """ Set defaults from the current config """ self.defaults = {} for section in self.sections(): secdict = {} for option, value in self.items(section, raw=self.raw): secdict[option] = value self.defaults[section] = secdict def reset_to_defaults(self, save=True, verbose=False): """ Reset config to Default values """ for section, options in list(self.defaults.items()): for option in options: value = options[ option ] self.__set(section, option, value, verbose) if save: self.__save() def __check_section_option(self, section, option): """ Private method to check section and option types """ if section is None: section = self.default_section_name elif not is_text_string(section): raise RuntimeError("Argument 'section' must be a string") if not is_text_string(option): raise RuntimeError("Argument 'option' must be a string") return section def get_default(self, section, option): """ Get Default value for a given (section, option) Useful for type checking in 'get' method """ section = self.__check_section_option(section, option) options = self.defaults.get(section, {}) return options.get(option, NoDefault) def get(self, section, option, default=NoDefault, raw=None, **kwargs): """ Get an option section=None: attribute a default section name default: default value (if not specified, an exception will be raised if option doesn't exist) """ if raw is None: raw = self.raw section = self.__check_section_option(section, option) if not self.has_section(section): if default is NoDefault: raise RuntimeError("Unknown section %r" % section) else: self.add_section(section) if not self.has_option(section, option): if default is NoDefault: raise RuntimeError("Unknown option %r/%r" % (section, option)) else: self.set(section, option, default) return default value = cp.ConfigParser.get(self, section, option, raw=raw) default_value = self.get_default(section, option) if isinstance(default_value, bool): value = eval(value) elif isinstance(default_value, float): value = float(value) elif isinstance(default_value, int): value = int(value) elif isinstance(default_value, str): pass else: try: # lists, tuples, ... value = eval(value) except: pass value = encode_to_utf8(value) if PY2 and isinstance(value, str): return value.decode("utf-8") return value def get_section(self, section): sect = self.defaults.get(section, {}).copy() for opt in self.options(section): sect[opt] = self.get(section, opt) return sect def __set(self, section, option, value, verbose): """ Private set method """ if not self.has_section(section): self.add_section( section ) if not is_text_string(value): value = repr( value ) if verbose: print('%s[ %s ] = %s' % (section, option, value)) cp.ConfigParser.set(self, section, option, encode_to_utf8(value)) def set_default(self, section, option, default_value): """ Set Default value for a given (section, option) -> called when a new (section, option) is set and no default exists """ section = self.__check_section_option(section, option) options = self.defaults.setdefault(section, {}) options[option] = encode_to_utf8(default_value) def set(self, section, option, value, verbose=False, save=True): """ Set an option section=None: attribute a default section name """ section = self.__check_section_option(section, option) default_value = self.get_default(section, option) if default_value is NoDefault: default_value = value self.set_default(section, option, default_value) if isinstance(default_value, bool): value = bool(value) elif isinstance(default_value, float): value = float(value) elif isinstance(default_value, int): value = int(value) elif not is_text_string(default_value): value = repr(value) self.__set(section, option, value, verbose) if save: self.__save() def remove_section(self, section): cp.ConfigParser.remove_section(self, section) self.__save() def remove_option(self, section, option): cp.ConfigParser.remove_option(self, section, option) self.__save() guidata-1.7.4/guidata/images/0000755000000000000000000000000012632514315014524 5ustar rootrootguidata-1.7.4/guidata/images/save_all.png0000666000000000000000000000307712601727200017026 0ustar rootrootPNG  IHDRw=gAMA a cHRMz&u0`:pQ<bKGDVIDATHǝˏ\Guϙx왉 c,LHV% $X0 +6(A@"F,0!q&I4'v^qOO?{T"9#]UΧ}{ᯜ87NիFf$ vxyUNaVVxϳMk,[}h޽{ߝ;ǿMɜLu+8*+8@UՈ8Dxɧ1} hل0x!`"۵րKƵ~` UJ"I@DpsFS_旿5G~}.ʢ"C)E- ]~p4ִt+W6jpwq4C񱟮G ~xωS1o,|"#D C@=d׻#Q(JPj0FmΫ,"V $ʢbZ *IX4js#MS# Y`1;]\$Qe NnJ{O EhZ[fLk1BŤҥW"9q""wE ( ]?q=GUU8H"ڔI1!xL&%шxh\0{H$O0dyy7 ^cj&BN CUUX[#a?ZTTBLPَ\J)r nuԕxa:%UUcdZcP5"bDI3V={nXrR3g9t| \pA)E7v\>zcǏ- [[uuSBls$>|~/e] Sn-1}};Sm`X+N<`C&* KS*%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/filesave.png0000666000000000000000000000217012601727200017027 0ustar rootrootPNG  IHDRw=gAMA a cHRMz&u0`:pQ<bKGDIDATHǝˋ\E{nOc8!N MpIH\H DA+ܻtޅ;7".ąBdKF#><\۝.֭s|_ٳoOǎ?ugϔWDVK)s;?|A_n@NFO;͛7˼.]z87_.# g2/]z_nnJIRp"a6@#0Sl !ܝNq+k $CfD?(j $@DO-ڮ`ڭY0Pe@1QKQm}.rDA@WS]2fK "U(?$徖h4_ޝۜ?’c~&\wbUF@o?CBF?wOwNzn̬l]CKeέm-tk׬}rKH:{?L"D -R(hT?hDpƟ2u9 D5T!ƈIoY5Td*JBiR.1.4lC$(Iꑐ' 9]ֶv?bV(hD} kuN|>͛{A$! a-5$207Coo/&&YXν$pE kEU%M\YhٳLjpeU˴ aCTQ[7 b1\0?y-sg8BV;ZŘvڹ0Ͷm̊) 184 1??GX\SիרvA>_̪Վi$k-"rD8i}5rVҠY晴s#[]"~ @IbsK a~qfg/R(@ H\ 4Mp< f)hoK/'ׯ1z=;w)d%tqI>rBX*mpQTJEɑNq9wnaK[+*^g]h>l`HT,1p^UN:ʼn03sbH2i~g xO!Ƹ^10NT*cEB{OzWusΑi!`~TT*72<[o291c줥}!S&.{KOEVB%=kU TMTTTu@{N+# kbmMMM_]0irUuMmo_~@}ĠB. g'A[nRXrCʚԛYs/Wl*|W]UM~eIծZj϶ʵ*>ݫwͤٓNvxY$==|E"[|[ʐU׸ݷröM&l5ٶ}]C??sg:?W>ͭw)?0ؓ2_<: >~~uNi? 4ō cHRMz%u0`:o_FbKGD̿ pHYs  tIMEURIDAT(͐0 E2 hSRg\ (#%ρ r&ĕ4naqR(<)* E3SP>w 0'Y1;@>6~?xW,4m'%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00Er7tEXticc:copyrightCopyright 1999 Adobe Systems Incorporated1lmtEXticc:descriptionDot Gain 20%4tEXticc:manufacturerDot Gain 20%tEXticc:modelDot Gain 20%2uIENDB`guidata-1.7.4/guidata/images/fileclose.png0000666000000000000000000000262012601727200017176 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_FPLTE7Pi.CZ2MkIhHgQp^}VuXw^~;Sl!-6?`<\{^}Qq0Op6Wx -7<]|]|Rr2Qs]| )%'5H$6#5 /E9]1Qs^{Xubb\{`ZyCbc\{]|[zGfKkYw;Wv ;Ic>]{=[zPj ?Rbv6Wv1Qq[k.9BU[c}6Tt-LkyV`jGWjh~YwPog}AUj%"! .'.)EVtLj.K *GfF]vH_x,Ii=]}NgOh;Z{4UuNgMe-KjE`{WpSkF`{XrdWxTtTllcE`|dSrQpZqJdaCdUoeZyYx\tUoJiUuYsngYtXwեԆЪВ֠twchwby`widwq_|[zHhKjca~JjXwXvNnB`?_?_~Cb8Wv;Zy7VuSq[x>]}=[z=\|=\{=]{\zEeGfLlb~gMmIhFe^{[xMlSrYxnmZyRr\yTnRrWwaxycWvSsUob}q}nb|tm]tRNS&+)NF52VO;LhbUnh3D,!0&,>BbOKbN\-R??@ABCDE FGHIJKLMNOPQRSTUVWXYZ[ \]^_`a  bcde!"#$%fghi&'(jklm)nopqrstuvwxyz{|}*~瀁+脅,-鈉.+/01-203…qmA̵%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/arredit.png0000666000000000000000000000214312601727200016663 0ustar rootrootPNG  IHDR DgAMA7 cHRMz&u0`:pQ<nPLTE~{|z{zzyyywxxuwwtvvsuurttq}}|ssrookmmillhkkgjjfiieiifmmkxxxooliighhehhfhhdggcdd`eeaYYTXXSVVQXXT\\[YYmm4eOKtRNSk%&($i}jbKGDKi PtIMEURwIDAT8˅WSP`aLB`/DJ n!od6Ei?084<2:6>19s$KU  0O#qD(ф e!!, & B]Xi"U edOcѼPPḛۻB0`ǀPS^y^61пI$Q1,fi @Ƞ JPP0ڧꠤ*ak[l6 @ %m 2(w+|@QJho )>ˍ.x]Ʀqy=hI%nZ<s/j}%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.30@GIENDB`guidata-1.7.4/guidata/images/file.png0000666000000000000000000000102212601727200016143 0ustar rootrootPNG  IHDR(-SgAMA a cHRMz&u0`:pQ<xPLTEbġf̔}yuqkho|֋ܘ楿tRNSǜ+!bKGD hVtIMEURdIDATm70#LdCSYA 2HDh"2fZ.?*acaXVX8c?`~&"%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.30@GIENDB`guidata-1.7.4/guidata/images/filetypes/0000755000000000000000000000000012632514315016530 5ustar rootrootguidata-1.7.4/guidata/images/filetypes/zip.png0000666000000000000000000000305112601727200020036 0ustar rootrootPNG  IHDR szzgAMA|Q cHRMz%u0`:o_FbKGD pHYs  +IDATXŗ[{.;;{񒵝8 p.F+HƆG)tf49՟sZ5LF5) GH/1*\eVcZl,¿XGʵXL|C%;%RrP!],o/^W_fB8 ⌻ %TQ ߍBWqbĘBg ȡұ#Z]3!FŇb7y@dHWc 1%RCpˎ}`.D읔L Jg zCjUYw%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/filetypes/xls.png0000666000000000000000000000232512601727200020045 0ustar rootrootPNG  IHDR DgAMA|Q cHRMz%u0`:o_FPLTE̷΋ל||##::QQee\[mm ''@@U\uHc7] ##;;LTA`3[']*pX$&G@e7N(L"HX*).C*E0=2'.\zS v1[0) TqS{22"+y v yHaG +3&̐`habmbxNNM 0;1%W < tQQ+j)AL܀6I]4%wufO?nRmE9m~',ft ˢ9t:/xj3"/67)?kƀ)%ii )2dYA%G`XE\\ymb[e=._ pK\vq?awWM,`{sFc^Yz/}͛O Q0 G5"EX&I"$i4M`0Y,d2s)y,d2y֭Gw*Ah 0<H^'I(`nf\8ϋj'0,[-2O,,\>wp0{V05/ 0=}ƹi_t14盛#at6Ba;;LpP) Ţ<0pls (@JeY|ry |QkB0V#19Fc󋲠z0?7hM&!?Y+WXgl76ז8;B|AM ӦRW=H2t|/~"^FЏ{;ވ^ E-еnXhPTU4M3^ak*~a)o+2( c:2PPuZSM4MC:V V C1QUng 0 |LLO!=-q_OYfd63hGyyl`!:i6J3tG/a6!ٚfb8o/ކ;ڌ4M蚉i R$B p>N#f[<ω$NHØ0)AgADb'6gڦ\yajwg 2v{(0$Sm cvwwH[]G/b&bMӨULӤ\.*Z-Dqp UkNF^c>Y<ڦZ^_gբӣ\*f~nl0N#R BЏCJaJ< ANINGH2E!'1Op2st(0a(NH )2ˈˆ(' i Tʀm,--zc;Be0dqZ-4!FS~ף>t2:F?[{%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/filetypes/pdf.png0000666000000000000000000000300712601727200020006 0ustar rootrootPNG  IHDR szzgAMA|Q cHRMz%u0`:o_FbKGD pHYs   IDATXŗkW{f%ZڨAB*]VveRZ-ݵ)n?ЊBAD EAWZ6))BELvFRܙvqfkJ̽g8>13_]sz ԁDUD@$H)VY*͇uGk$MFU3#jREO=M}(LTP ("*DJ'ՍY1c>( sXmla+dHٍ:A"EŊ[J  C l?'м@DPSixW@ 'aRG`emH5 HABDTe``NI, o/jS phFY|128хȤ!I0 qU Z[) 2 skQ24;=/DA Rx,{p=ۼ҂okuu/|j4Np ܒx!ԩE(҄HYnqw7V_߸1RrOQ|WW @@HB*6 1|Ha) wt 7o\i F|\Od۶4k~&dxv/"V5Zl!k$ħO3}z* իIolrTBzNL1q\Y `1~f] ,W|qmmdj^lŊJ¨- =y(£ IL>%ݰgOay{O=i֬;A"ب\AV{C,;~ݻ?KH +Zq uK$%R(aPojbYg''N4’4rAE3_T3'$7!SoM c0M `2-ͺPU%iΓ`xlk2 :<Q!4|uu&kERmCDUyP? \;n1CY:<5ȦrG%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/filetypes/tif.png0000666000000000000000000000275012601727200020023 0ustar rootrootPNG  IHDR szzgAMA|Q cHRMz%u0`:o_FbKGD pHYs  IDATXŗ]hUw>vfWئ mkJ+*j}HJQQP&P 6BJ/RMmHlI6d7ݝ{}()fR/,ws+R(M!PJ!D4? !VB@={ږ@,(Kɖ@H)QJib ]m $+ ^)lI _/4&$7CPI%@zBh[ӵJ 2cyT6@ƾFh`쯘+Fy5LdٓX/ZUFKF \x?P4sAVnE6 Zf]C n:0JyhBN&uv&jBS=@>g/(VN^9?}N>_O$AA[{=в,a9ojw ~ 69^pIxi vc=zp]hl(‘Sr )i`"[`:Ќ꿄 úLNQ|8y6GyA)Bsݳį I !?;| ~8X=n>uy7ȷI)Eh<2fXdri;I` i7bxIjӌ% RIs s HJC \qjZb 3xe '0 4`U`61MIak=lsO*ۄuƆG)tf49՟sZ5LF5) GH/1*\eVcZl,¿XGʵXL|C%;%RrP!],o/^W_fB8 ⌻ %TQ ߍBWqbĘBg ȡұ#Z]3!FŇb7y@dHWc 1%RCpˎ}`.D읔L Jg zCjUYw%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/filetypes/gif.png0000666000000000000000000000275012601727200020006 0ustar rootrootPNG  IHDR szzgAMA|Q cHRMz%u0`:o_FbKGD pHYs  IDATXŗ]hUw>vfWئ mkJ+*j}HJQQP&P 6BJ/RMmHlI6d7ݝ{}()fR/,ws+R(M!PJ!D4? !VB@={ږ@,(Kɖ@H)QJib ]m $+ ^)lI _/4&$7CPI%@zBh[ӵJ 2cyT6@ƾFh`쯘+Fy5LdٓX/ZUFKF \x?P4sAVnE6 Zf]C n:0JyhBN&uv&jBS=@>g/(VN^9?}N>_O$AA[{=в,a9ojw ~ 69^pIxi vc=zp]hl(‘Sr )i`"[`:Ќ꿄 úLNQ|8y6GyA)Bsݳį I !?;| ~8X=n>uy7ȷI)Eh<2fXdri;I` i7bxIjӌ% RIs s HJC \qjZb 3xe '0 4`U`61MIak=lsO*ۄus /]B6ADɾbLa,(حDDL224Ҕ0(ahruyyNsIU::vR8$u, -ԥt, P(*(**(ɭNYmr 8|}*|e*؉W `B %tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/filetypes/html.png0000666000000000000000000000347012601727200020205 0ustar rootrootPNG  IHDR szzgAMA|Q cHRMz%u0`:o_FbKGD pHYs  :IDATXŗ[h\3겫Jrd;VnBRRJ!$(O)BB܇҇>4B[h@z I\;rZ]I{;,g#d`9} c N\xqI15RJBhCa Oy\D&a ~><~ \*?Xɳ#|c}frȫ}gH M5P[ ]Թ/\nqTo<3dFTl5a)|^xr\o|-,ٵ!ЩKOLXgq%Ui3XmJ)<2’@_@YzKscjrcKֶcok&?}X_6cd}PSٌX܈!x,_},VR[+-޽]Wɯq/Ɩ[Q85M`ےzJ+ -zaT׹m00^s{i !Wy\<[ T)\T?zͰVOz{m޻#c Fxl4;n҈֚6|)hdž0LI)x ̈́J`9s,o~YF($z.6 )h c A6Cy6V@l4;y={5`)Ou;&5nVP r'Q1~O24P.eDcN{(+5yߦ6`t(p DZ6SZבdI/ZR4ĊO1C8H8`O0KscBЈd]4}8RTXocuR"Xĵ%R%A R`8E t\t tj;CD!xG%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/filetypes/png.png0000666000000000000000000000275012601727200020025 0ustar rootrootPNG  IHDR szzgAMA|Q cHRMz%u0`:o_FbKGD pHYs  IDATXŗ]hUw>vfWئ mkJ+*j}HJQQP&P 6BJ/RMmHlI6d7ݝ{}()fR/,ws+R(M!PJ!D4? !VB@={ږ@,(Kɖ@H)QJib ]m $+ ^)lI _/4&$7CPI%@zBh[ӵJ 2cyT6@ƾFh`쯘+Fy5LdٓX/ZUFKF \x?P4sAVnE6 Zf]C n:0JyhBN&uv&jBS=@>g/(VN^9?}N>_O$AA[{=в,a9ojw ~ 69^pIxi vc=zp]hl(‘Sr )i`"[`:Ќ꿄 úLNQ|8y6GyA)Bsݳį I !?;| ~8X=n>uy7ȷI)Eh<2fXdri;I` i7bxIjӌ% RIs s HJC \qjZb 3xe '0 4`U`61MIak=lsO*ۄuvą:J`>񤢢kvm`cga^pH򱯭Gle]Ql[b🟟b댾씽뎹xuoۓډ։֊نىӠ~ͭ퐐)tRNS帹b;bKGDH pHYs  tIMEURIDAT8ˍwPۚuv#}"lmwVl;  lh,nr C/w9'99لqGKy@A%Y,DQUp>h0"H{@|ES2,{jz { L{`((%)LPEV۴m׾CNJN=2:Kn{ջO__z 4xPT c LjeUU.qML8iӦϘY-Hx651o/Yl9Vd+VXv 7m޲dvT0sW={?p2@:#GM;~gΞ; T ~eW^~w$,h><|g_+QdPaں 0#ۡ#>/ Jip8W7D?uBw,+B`@vfWئ mkJ+*j}HJQQP&P 6BJ/RMmHlI6d7ݝ{}()fR/,ws+R(M!PJ!D4? !VB@={ږ@,(Kɖ@H)QJib ]m $+ ^)lI _/4&$7CPI%@zBh[ӵJ 2cyT6@ƾFh`쯘+Fy5LdٓX/ZUFKF \x?P4sAVnE6 Zf]C n:0JyhBN&uv&jBS=@>g/(VN^9?}N>_O$AA[{=в,a9ojw ~ 69^pIxi vc=zp]hl(‘Sr )i`"[`:Ќ꿄 úLNQ|8y6GyA)Bsݳį I !?;| ~8X=n>uy7ȷI)Eh<2fXdri;I` i7bxIjӌ% RIs s HJC \qjZb 3xe '0 4`U`61MIak=lsO*ۄuƆG)tf49՟sZ5LF5) GH/1*\eVcZl,¿XGʵXL|C%;%RrP!],o/^W_fB8 ⌻ %TQ ߍBWqbĘBg ȡұ#Z]3!FŇb7y@dHWc 1%RCpˎ}`.D읔L Jg zCjUYw%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/filetypes/ps.png0000666000000000000000000000262412601727200017663 0ustar rootrootPNG  IHDR DgAMA|Q cHRMz%u0`:o_FFPLTE½Ʋⴵ앖ξ߱򳳴񐎎YWVUSTROONKKHFEB@?󵵶񵶷VRSUQRSOPQNNNKL7556233012./.+,&""޹ONOSRTTRTVUWVVWVVXWVXPOPࠡ~?>@HFIHFH@?A}~䊊xxxkkkzzzwwwyyyabaݎЇDZrktRNS帹b;bKGDH pHYs  tIMEURIDAT8c` 0212323hF& +`@(`$IR@R@I(P$+B8+ %-##+@BB⒒ bbP$& 4 MBR\\RYQEE ԁcC !.!.! Z@w#LE("6$:( DQ +Y-v;)I{G'gGGW7wOO/Io>~A!!aaQH`OHLJNIIMǨFV_PPXT\RZZV^QYUU]c! YA76pQ=}&ILfGR6m3gAٳ̝'`/Y ˖Xr WYn``i-ّlYev60غJGB\@Ʉ-lٱS wHp")ز{18ؾGGBRa-wݷ8x : lY:;''&NUǮݻ0T4/s}ɱ]%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/shape.png0000666000000000000000000000055112601727200016332 0ustar rootrootPNG  IHDRbgAMA a cHRMz&u0`:pQ< PLTE&PYtRNSbKGD LtIMEUR(IDATc`PBUV0@X °DY p1,bu ! 'р%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.30@GIENDB`guidata-1.7.4/guidata/images/editors/0000755000000000000000000000000012632514315016175 5ustar rootrootguidata-1.7.4/guidata/images/editors/filesave.png0000666000000000000000000000217012601727200020500 0ustar rootrootPNG  IHDRw=gAMA a cHRMz&u0`:pQ<bKGDIDATHǝˋ\E{nOc8!N MpIH\H DA+ܻtޅ;7".ąBdKF#><\۝.֭s|_ٳoOǎ?ugϔWDVK)s;?|A_n@NFO;͛7˼.]z87_.# g2/]z_nnJIRp"a6@#0Sl !ܝNq+k $CfD?(j $@DO-ڮ`ڭY0Pe@1QKQm}.rDA@WS]2fK "U(?$徖h4_ޝۜ?’c~&\wbUF@o?CBF?wOwNzn8s[?=89:=hefgbmGjf\wPuHl;tIǒg2]+]/\`0\^-yQ[*^G,26?889:::Z(򚚛Y'X&W%~V$~U"}T!}T!vLxP|S |R |R uI|lPSNBz|z{{aaaٰ{aVخźŹŹ˿Ƚy]|Lѩe}LzGjCd@b;a=kHkCͣ{•g}{ysr͝tvxD]꛾șp_8Q@O[YlR{TE;UragQ}I`{h~\azFl[# !~j\vEm:b-s`ZtCsBf.xZvPh=e``gᒐbCR!,1CE -j*(P'RT 2cz%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/editors/editcopy.png0000666000000000000000000000203012601727200020515 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_FPLTE;;:wwwvvv000eegPPP:::LLLDDEMMLTTUMMM***xxykkk墣NNM444ݡNNNǟRRRAA@&&&bbbfffUUUlllqrtLMNTUU@@A%%%?>?))*GGG677ǵGPtRNS: pU3 Þ|V3 ͖"~θuV72tN1 ݝdhbKGDH pHYs  tIMEURIDATc`F&fV66vN.7 ED@ 8@LA" $4,< $e"cb ^LAN> !1&) RR32 KEA5 ;'7/ ԀEũ%`P*Hͯ*m]Z07``04ohllljln16a`05kmk,*1``0qdk[;{89{x2?d H^L%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/editors/plot.png0000666000000000000000000000100312601727200017652 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_FZPLTELDeLDeiSr>>̀*+ xdtRNS4&bKGDH pHYs  #utIMEURIDAT]0E*_&jvMN)m?SJy. 8%\wZ: rE;`uqƊ{$ 2Rahcwrhγ5^~'+߾^ SL%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/editors/edit.png0000666000000000000000000000164112601727200017631 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_F\PLTEĠssrQPQ@>?~|}eee FFFܰأߜ󳱱ߡб񰰳ċ5tRNS; o[<@CCCCCCCCF8cbKGDH pHYs  tIMEURIDAT-gWP UT{p EU?ǴoyΛ7PQYeFuM$R[W+?&RCsEDH- Yen"a(shuMpKxҹnvpexn~wo\} O^zN/.uN9)[UUww𨋚3!<=^K7" GMKFF?}LNM%Tjaqi`46%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/editors/editdelete.png0000666000000000000000000000265512601727200021022 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_FPLTEv]r\[30h=q^mtKFDGH-12$73;852 :6=:ZS^YE>s N<y IBy!F2T@^ /K Sij= Up{iaJbanz ta$_g KZwK_%Muvv mF  !2 ,8hp"#(DddF)%z¼ÿnhlfz{vkd84>;kfwoMHHC:4F>E@MF&"$"805.   ~`d w s        2(E5 72NGPH:3,tRNS^s N[e|CX dAgTm Ȑ fש Ȏ9sS}7ܭQSl>RC|Oz:bKGD۶x pHYs  tIMEURIDAT()*+,-./0123 4567  89:;<=>? @ABCD EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ツ뇈 !"# !$%&'scO!x%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/editors/edit_add.png0000666000000000000000000000175112601727200020443 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_FPLTE;r>:q=?yBFGGJ@yC8m;7n:4f83i75j84f76h:;q>;l?:e>5f93f7-Y1b_QP3b86k95j94j87m;:q=9z:h^YR@yA1{1C~C0v1@@7v8 <1>y?7h:6h:8m<>lA;g?>jA:f>:f?;q>9x:2254:{;MMeakhRR_\r|ed^]ympg\[d`xk_XUtaR95E=[Vmiif`NN?SP[W_\eb='bRp`VD=/;.G7**(A:*)"@733+<8?tRNS#,,***##",***LL***,"",***LL**,##***,,#h 7xbKGDi+9 pHYs  tIMEURIDATc`F&fVF`wptbGpvquDpBばyDD%$CBee#"cbSR32sr KJʕ+*kTjT54ut[Z Vwt ô YDs$ILD3,%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/editors/imshow.png0000666000000000000000000000132712601727200020213 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_F)PLTE]dbdcefegfhbgkmlnomqrqksyxy{x}~~z}SGqqptcev:DLe`NPV@8+!^gosNSYXkE@V`8?U(2?7QvsMGP1"64#,tPg:1?bM]@C*chBxpx~TdqQQWAQXHjNU5OSBZ tRNS@fbKGDH pHYs  tIMEURIDATc` # 0sprqED%$R2r J*`5u M-m]=}C-X,  OHd0t,aD%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/editors/fileimport.png0000666000000000000000000000431012601727200021052 0ustar rootrootPNG  IHDR szzgAMA a cHRMz&u0`:pQ<bKGDIDATXÍkU^ffgRz" TTDF BEEX j _`LhJ%BDkp)b(R*]-nwf9fw9QkVɃ¼zؾBj4nZ!Ԗ\׻gO+(ԩ9`l՚޶|$nB(" ) zz*+}ae9%ϟ}o@S!eƛchH|JѯskW=V"JtQM6-x7vh-t&(LrRjTN%iCaǎ]-BG 2RJy*U?^1QY,Vg>PDP2 #s25'3 33gs\M&\ pB)uA!^-v^;YqNɖ=Ac2JT*W ,f T jmث >|SŒs!-*JMi&QԨ'3fÀ @Pň^VxsgR'@+%ꎩJ)b&.ԨJ| Cāf)W UY' (Gb[ѱjlMHjoH黬&l ?uhAPDiCĄ& },~T-hD' }((ݝ0X.(k-1(Xzc Q `c'J+2h1JP% Zy5g6sy9F ã~tPgGX@D q|ANJFByGĜa){ BTr]xOB 1JBrPf̵SnOpO2/s-_ Z.EB21Ffp<Ҙq.ċٍmO )ѦPiH. gWs{/cuu"A*BQ.paF {"C)`J0 & Y~o3VRk1E}u#;~I[$EOє&OH|B" Ir$Ɖ=.dIJ >,H31N/%Zc5λf뾊BWvRM6pݣqq/@OS'ec[olY ɢ DmV~AmQ%`j a=lYw[bCW0A̿|7p%JVƽ$7a8 [WA>BVX,7|5qXbpp9iKW}-ޡ330`TǟJ!8sʯQJl9/Gi8VdopG^ `ؾ}"pjƻ.Z5k .ް>n_Ak5ysw2F-H[s9I=ByL/Q~g#V#ڃR̎CwLxA,%qx?%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/editors/rename.png0000666000000000000000000000126112601727200020151 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_FPLTEfff+f8tRNS4hbKGDH pHYs  d_tIMEURIDATӅY@ᙲE 3" I";Yw:90 #%!$S錐͉ RYTk NhCojFlw,?0`LeK3/Bťlnee;zXi0 E,$KdqW%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/xmax.png0000666000000000000000000000054212601727200016207 0ustar rootrootPNG  IHDRbgAMA a cHRMz&u0`:pQ< PLTE%PY[tRNS+NbKGD LtIMEUR!IDATc`@0D,`Z JP :dB#1NS%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.30@GIENDB`guidata-1.7.4/guidata/images/none.png0000666000000000000000000000051312601727200016167 0ustar rootrootPNG  IHDR7gAMA a cHRMz&u0`:pQ<tRNSv8bKGD݊ pHYs ;ttIMEUR IDATc` 0Ǫ%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.30@GIENDB`guidata-1.7.4/guidata/images/delete.png0000666000000000000000000000132212601727200016471 0ustar rootrootPNG  IHDR(-SgAMA7 cHRMz&u0`:pQ<PLTE7JS!%,r wLsS+ 5T65sJ&]?B( dD&:_@F, GnO_AK0VC%mO":^@;"  +pfHsvXU86M¡9tRNS'kd*.+0k{+E ,bKGDB=tIMEURIDATӍ@DWErP]JA5O)#TzUP \f4f '/ $J4%浛=;dz`wiv <0(98IϏ0~gy(7(>eघ?s U-U}3 b9a%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.36%IENDB`guidata-1.7.4/guidata/images/max.png0000666000000000000000000000054412601727200016021 0ustar rootrootPNG  IHDRbgAMA a cHRMz&u0`:pQ< PLTEPYʆtRNS+NbKGD LtIMEUR#IDATc`P%  b:ci%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.30@GIENDB`guidata-1.7.4/guidata/images/busy.png0000666000000000000000000000132112601727200016210 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMR@}y<s?~|}eee FFFܰأߜ󳱱ߡб񰰳ċ5tRNS; o[<@CCCCCCCCF8cbKGDH pHYs  tIMEURIDAT-gWP UT{p EU?ǴoyΛ7PQYeFuM$R[W+?&RCsEDH- Yen"a(shuMpKxҹnvpexn~wo\} O^zN/.uN9)[UUww𨋚3!<=^K7" GMKFF?}LNM%Tjaqi`46%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/copy.png0000666000000000000000000000147612601727200016213 0ustar rootrootPNG  IHDR DgAMA a cHRMz&u0`:pQ<PLTE䦧᜜混紴ﵵ㰰ޭಱ`tRNS3MbKGDH pHYs  tIMEURKIDAT8˅r0am+ILUT\@hݷ&MD%|sX(pAMs v$B9gP .ʹ‹^]^? Dhu0[XO)V#&' `9EaA~c%o1qŐZ`Q"򕕬hZf+n@+K)-d dM MrY_&,5)+1(')洿ᦽ菾呿ૻǯ쌻㎻㍻সƪ鋸ኸॷƨ臵އ݈ߤŨ牴܀$ɹutRNS5[r0@Ru0SRRRRRRRRQ[9O\&)$g 73q@B{о.Nߖ^q+dn5ap @+ dl  ebKGDHtIMEURIDAT(c`@L,lN.n^>~AҲ aheT7%RM-mp J 뗂KHOKL4yiӦ%d!3&M9krp yP .ZD.$l+W*%AVYn*p M![n۾C .vٻo FġG;ОN0uRjէ_}|΁AUVа(ccbSR32ˇ  %C%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/cell_edit.png0000666000000000000000000000126112601727200017155 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_FPLTEfff+f8tRNS4hbKGDH pHYs  d_tIMEURIDATӅY@ᙲE 3" I";Yw:90 #%!$S錐͉ RYTk NhCojFlw,?0`LeK3/Bťlnee;zXi0 E,$KdqW%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/python.png0000666000000000000000000000156712601727200016563 0ustar rootrootPNG  IHDR(-SgAMA a cHRMz&u0`:pQ<nPLTEZZZ[[[I}@pMs@yAkDfIf?rAlCgEc}Ec|ɢ?|>t@nBiEd~Ge}a\G=u@oBjDeFd}[OP=v?qBktRG@FjEd}`VK@9aZODe\Q?]HIDA'tRNS% !)*#P; (#PbKGD-tIMEURIDATc`F&ufV8`a prqԍMLy`L]h#N'gW733wO/o9~@MA!| aQ11q < I)@~jZzFf0zLL6*.q II)nܼ|uiY9S K.U(-+R ()H"+ͭ%%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/selection.png0000666000000000000000000000135012601727200017215 0ustar rootrootPNG  IHDR(-SgAMA a cHRMz&u0`:pQ<PLTE/Ɯˑˍ˔˞egsY[hTVcLN\CETgitz|KM[02BsuˌlnyCET-Z\i24Eϴceqʔ𛝤홚򒔜񌍖ӎ|}Y[gÃČabnxzfhsqs~#$tRNS/;FD50yrJYk3QH"ɔɈabKGD.TtIMEURIDATc````Tab@,jl(Z(:\zz<(چF\H&F3M-,١v1Z6vP{G'g>~AO/aoQ14L@FVA^!0HŇJ! h(%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.36%IENDB`guidata-1.7.4/guidata/images/quickview.png0000666000000000000000000000157012601727200017243 0ustar rootrootPNG  IHDR(-SgAMA|Q cHRMz%u0`:o_FYPLTEffffffffffffffffffffffffffffffUUUUUUffffffUUUQQQMMMUUUUUUPPPUUUfffUUUJJJIIIzzzPPPUUUiiiNNNpppfffAAA555111;;;KKK&,3^ys]9RMMMYB$1tFFFSoE2躺OXk???333c6;dFOX_7CCC솺Yim?b漼===ĖE8ikkkꬬڷ&tRNSo/_O??/_Oϯo?oobKGDH pHYs  tIMEURIDATc````dbffa@V65 `g9ffv56 , R<<@x"| ZB `ut 5DD98%ML-,5$E9xml%EY8x\\=<}|"| RA^!a@CEcbSd@ȦgdfgɁ'._^Q *(T) l%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/min.png0000666000000000000000000000053712601727200016021 0ustar rootrootPNG  IHDRbgAMA a cHRMz&u0`:pQ< PLTEPYʆtRNS+NbKGD LtIMEURIDATc`@| Y14B c9ԧ%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.30@GIENDB`guidata-1.7.4/guidata/images/exit.png0000666000000000000000000000221412601727200016201 0ustar rootrootPNG  IHDR(-SgAMA a cHRMz&u0`:pQ<CPLTEVP\p7gp7hQ ]VW,Mtryw0PW'R-2/3'QY#Xw4v1WVDFCDV :U 8Q 9K 9>K9Ig fq=m7*s(- %*s!0p48,1!/o9|B]tssrAZ:}KB]bqbqC^L_PfX0"IAibngNF/" ;32) 祡ᐌ70矛 "魪2, up@9 /'.' ?9rnxtA:$#@9vr{B<ꢞ&&衞>7쨥rlrl餡 "2),$.!91_W^W4,%H:xC5tf=tRNS'ab'##;}8$#8b?x0!NXB91LJG\x&~VUQIGc{Klgѵ9ZQGr A>l*w"v9%(ll*?%C8{?'?!߁Xj*^.XC%ݣ|Th`"X "8ê\WhmN2范(+N.>a7|Nc9EpKX4o˔k ^aގ|xGJT(:5a \5;ʐ䰦-UUuhgm3 O ?S8nՒ#),&ju #7w8Vn\}n}A?G ݽ~,ٮɧ/2-7 `o &"!Zc$20YScG';DrCD8& .\ҼPW~BLxyE> ɤJʀrBLI kpfҮ䈵/Su~<1l5`/SH&KNmw>VUcMH퍺/76E:9Ԗѳ[o6^zj5;3⍵A>|ٰ~K``IˤZ8be'svcFs'85 \6V^(tbys|D_t[\(<EpDjs*)ޚS?_Y ̬?RHƞMGڱ58vvk-{˪(Mכ8dqN,Bk^>ˀyG7{G#>Xl^9MthgfTLh,XmXq6+y9 =7*wU54x*8\W*1=h@ Bm5L 2^! H}zItkґqTO"JeT?۹K[Kg7nwPcnPLTEƙƥUUU~~||hhlhhi```^^^jlxdfnegoUUUaciabi000\]j]_mEEEacsEEE\^k555 NPYQR\ͼӘؾ֡ݽٝح¥ҥѣĬҢ̡̬ӫ¢ŋřƬƔuxǰȮǏtwx}uy}|txy}bduPtRNS O= AE堝NV建 a_ *WodcnE"  '+-,%K7bKGD k=tIMEURIDATc`Ya|V6v.v6V'0(8$4>~ȨXA!~>H\|BbRRrJ\0HhZzFxVvN+)_P( ST\R* ++W`P(iQSVohlRU՚[Z::T=}}&jih3L5Y?VGu*A3N1]W$CH o~zj 1^!eȏbal3@؁qȠ(Gz멍֮_+pW]J| xT#IJx1g-iF))hDj)WBaڂf++i&i@ RuuW5}BY3]d gkuKeC3["#9>dW_#$4M ]@a trٍmFr 뀶9^>:gpwNyR͜~,cPE7tG'Ґ'(;Ջ<|)mXN/9{r9TUm%&,9\\~ԛ|)c/"N jR?a߭M;}~H6*=q<О挙& #8};_.d6P=ۯ;gh7:,/h.0r=|k&i%Z4/5i$ ݜSOm=>xhvuIG:>`ط_y- / P(™{Ip@0KXs%`OH1շ&!xka , j^ES `]ՠ`@yk 1mY}** `7#Ft㳎Q?;&fc[CvsT)ll.%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/images/guidata.svg0000666000000000000000000004560512601727200016674 0ustar rootroot image/svg+xml guidata-1.7.4/guidata/images/xmin.png0000666000000000000000000000054112601727200016204 0ustar rootrootPNG  IHDRbgAMA a cHRMz&u0`:pQ< PLTE%PY[tRNS+NbKGD LtIMEUR IDATc`@p DpU@ .\*7s $3%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErtEXtSoftwarePaint.NET v3.30@GIENDB`guidata-1.7.4/guidata/images/apply.png0000666000000000000000000000142112601727200016354 0ustar rootrootPNG  IHDR(-SgAMA a cHRMz&u0`:pQ<5PLTE v] xMwE wS H:4D?  JF ID5/"LHRf](#%} WfO0$ bO0$l +T@6rI:XWN"t rd _ #gY׺]V׶ZRA:QIܴZQ `U% ݵکj`7(/"yoߝߘtiI9=1_S䈖厥|qYJL?^OxygXVJo`xieXw;tRNS ͷ д Ѳ5 A <   #lbKGDHtIMEURIDATc`L,(|V6kv$>9:9 00 00{K00HJyzIy+*** S ԂP )9 &f(4Dnd X%tEXtdate:create2015-08-31T15:00:15+02:00qH%tEXtdate:modify2015-08-31T15:00:15+02:00ErIENDB`guidata-1.7.4/guidata/disthelpers.py0000666000000000000000000011013312627660532016171 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2011 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) # pylint: disable=W0613 """ disthelpers ----------- The ``guidata.disthelpers`` module provides helper functions for Python package distribution on Microsoft Windows platforms with ``py2exe`` or on all platforms thanks to ``cx_Freeze``. """ from __future__ import print_function import sys import os import os.path as osp import shutil import traceback import atexit import imp from subprocess import Popen, PIPE import warnings # Local imports from guidata.configtools import get_module_path from guidata.py3compat import to_binary_string #============================================================================== # Dependency management #============================================================================== def get_changeset(path, rev=None): """Return Mercurial repository *path* revision number""" args = ['hg', 'parent'] if rev is not None: args += ['--rev', str(rev)] process = Popen(args, stdout=PIPE, stderr=PIPE, cwd=path, shell=True) try: return process.stdout.read().splitlines()[0].split()[1] except IndexError: raise RuntimeError(process.stderr.read()) def prepend_module_to_path(module_path): """ Prepend to sys.path module located in *module_path* Return string with module infos: name, revision, changeset Use this function: 1) In your application to import local frozen copies of internal libraries 2) In your py2exe distributed package to add a text file containing the returned string """ if not osp.isdir(module_path): # Assuming py2exe distribution return sys.path.insert(0, osp.abspath(module_path)) changeset = get_changeset(module_path) name = osp.basename(module_path) prefix = "Prepending module to sys.path" message = prefix + ("%s [revision %s]" % (name, changeset) ).rjust(80 - len(prefix), ".") print(message, file=sys.stderr) if name in sys.modules: sys.modules.pop(name) nbsp = 0 for modname in sys.modules.keys(): if modname.startswith(name + '.'): sys.modules.pop(modname) nbsp += 1 warning = '(removed %s from sys.modules' % name if nbsp: warning += ' and %d subpackages' % nbsp warning += ')' print(warning.rjust(80), file=sys.stderr) return message def prepend_modules_to_path(module_base_path): """Prepend to sys.path all modules located in *module_base_path*""" if not osp.isdir(module_base_path): # Assuming py2exe distribution return fnames = [osp.join(module_base_path, name) for name in os.listdir(module_base_path)] messages = [prepend_module_to_path(dirname) for dirname in fnames if osp.isdir(dirname)] return os.linesep.join(messages) #============================================================================== # Distribution helpers #============================================================================== def _remove_later(fname): """Try to remove file later (at exit)""" def try_to_remove(fname): if osp.exists(fname): os.remove(fname) atexit.register(try_to_remove, osp.abspath(fname)) def get_msvc_version(python_version): """Return Microsoft Visual C++ version used to build this Python version""" if python_version is None: python_version = '%s.%s' % (sys.version_info.major, sys.version_info.minor) warnings.warn("Assuming Python %s target" % python_version) if python_version in ('2.6', '2.7', '3.0', '3.1', '3.2'): # Python 2.6-2.7, 3.0-3.2 were built with Visual Studio 9.0.21022.8 # (i.e. Visual C++ 2008, not Visual C++ 2008 SP1!) return "9.0.21022.8" elif python_version in ('3.3', '3.4'): # Python 3.3+ were built with Visual Studio 10.0.30319.1 # (i.e. Visual C++ 2010) return '10.0' else: raise RuntimeError("Unsupported Python version %s" % python_version) def get_dll_architecture(path): """Return DLL architecture (32 or 64bit) using Microsoft dumpbin.exe""" os.environ['PATH'] += r';C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\BIN' process = Popen(['dumpbin', '/HEADERS', osp.basename(path)], stdout=PIPE, stderr=PIPE, cwd=osp.dirname(path), shell=True) output = process.stdout.read() error = process.stderr.read() if error: raise RuntimeError(error) elif 'x86' in output: return 32 elif 'x64' in output: return 64 else: raise ValueError('Unable to get DLL architecture') def get_msvc_dlls(msvc_version, architecture=None, check_architecture=False): """Get the list of Microsoft Visual C++ DLLs associated to architecture and Python version, create the manifest file. architecture: integer (32 or 64) -- if None, take the Python build arch python_version: X.Y""" current_architecture = 64 if sys.maxsize > 2**32 else 32 if architecture is None: architecture = current_architecture assert architecture in (32, 64) filelist = [] msvc_major = msvc_version.split('.')[0] msvc_minor = msvc_version.split('.')[1] if msvc_major == '9': key = "1fc8b3b9a1e18e3b" atype = "" if architecture == 64 else "win32" arch = "amd64" if architecture == 64 else "x86" groups = { 'CRT': ('msvcr90.dll', 'msvcp90.dll', 'msvcm90.dll'), # 'OPENMP': ('vcomp90.dll',) } for group, dll_list in groups.items(): dlls = '' for dll in dll_list: dlls += ' %s' % (dll, os.linesep) manifest =\ """ %(dlls)s """ % dict(version=msvc_version, key=key, atype=atype, arch=arch, group=group, dlls=dlls) vc90man = "Microsoft.VC90.%s.manifest" % group open(vc90man, 'w').write(manifest) _remove_later(vc90man) filelist += [vc90man] winsxs = osp.join(os.environ['windir'], 'WinSxS') vcstr = '%s_Microsoft.VC90.%s_%s_%s' % (arch, group, key, msvc_version) for fname in os.listdir(winsxs): path = osp.join(winsxs, fname) if osp.isdir(path) and fname.lower().startswith(vcstr.lower()): for dllname in os.listdir(path): filelist.append(osp.join(path, dllname)) break else: raise RuntimeError("Microsoft Visual C++ %s DLLs version %s "\ "were not found" % (group, msvc_version)) elif msvc_major == '10': namelist = [name % (msvc_major + msvc_minor) for name in ( 'msvcp%s.dll', 'msvcr%s.dll', 'vcomp%s.dll', )] windir = os.environ['windir'] is_64bit_windows = osp.isdir(osp.join(windir, "SysWOW64")) # Reminder: WoW64 (*W*indows 32-bit *o*n *W*indows *64*-bit) is a # subsystem of the Windows operating system capable of running 32-bit # applications and is included on all 64-bit versions of Windows # (source: http://en.wikipedia.org/wiki/WoW64) # # In other words, "SysWOW64" contains 32-bit DLL and applications, # whereas "System32" contains 64-bit DLL and applications on a 64-bit # system. if architecture == 64: # 64-bit DLLs are located in... if is_64bit_windows: sysdir = "System32" # on a 64-bit OS else: # ...no directory to be found! raise RuntimeError("Can't find 64-bit DLLs on a 32-bit OS") else: # 32-bit DLLs are located in... if is_64bit_windows: sysdir = "SysWOW64" # on a 64-bit OS else: sysdir = "System32" # on a 32-bit OS for dllname in namelist: fname = osp.join(windir, sysdir, dllname) if osp.exists(fname): filelist.append(fname) else: raise RuntimeError("Microsoft Visual C++ DLLs version %s "\ "were not found" % msvc_version) else: raise RuntimeError("Unsupported MSVC version %s" % msvc_version) if check_architecture: for path in filelist: if path.endswith('.dll'): try: arch = get_dll_architecture(path) except RuntimeError: return if arch != architecture: raise RuntimeError("%s: expecting %dbit, found %dbit"\ % (path, architecture, arch)) return filelist def create_msvc_data_files(architecture=None, python_version=None, verbose=False): """Including Microsoft Visual C++ DLLs""" msvc_version = get_msvc_version(python_version) filelist = get_msvc_dlls(msvc_version, architecture=architecture) print(create_msvc_data_files.__doc__) if verbose: for name in filelist: print(" ", name) msvc_major = msvc_version.split('.')[0] if msvc_major == '9': return [("Microsoft.VC90.CRT", filelist),] else: return [("", filelist),] def to_include_files(data_files): """Convert data_files list to include_files list data_files: * this is the ``py2exe`` data files format * list of tuples (dest_dirname, (src_fname1, src_fname2, ...)) include_files: * this is the ``cx_Freeze`` data files format * list of tuples ((src_fname1, dst_fname1), (src_fname2, dst_fname2), ...)) """ include_files = [] for dest_dir, fnames in data_files: for source_fname in fnames: dest_fname = osp.join(dest_dir, osp.basename(source_fname)) include_files.append((source_fname, dest_fname)) return include_files def strip_version(version): """Return version number with digits only (Windows does not support strings in version numbers)""" return version.split('beta')[0].split('alpha' )[0].split('rc')[0].split('dev')[0] def remove_dir(dirname): """Remove directory *dirname* and all its contents Print details about the operation (progress, success/failure)""" print("Removing directory '%s'..." % dirname, end=' ') try: shutil.rmtree(dirname, ignore_errors=True) print("OK") except Exception: print("Failed!") traceback.print_exc() class Distribution(object): """Distribution object Help creating an executable using ``py2exe`` or ``cx_Freeze`` """ DEFAULT_EXCLUDES = ['Tkconstants', 'Tkinter', 'tcl', 'tk', 'wx', '_imagingtk', 'curses', 'PIL._imagingtk', 'ImageTk', 'PIL.ImageTk', 'FixTk', 'bsddb', 'email', 'pywin.debugger', 'pywin.debugger.dbgcon', 'matplotlib'] if sys.version_info.major == 2: # Fixes compatibility issue with IPython (more specifically with one # of its dependencies: `jsonschema`) on Python 2.7 DEFAULT_EXCLUDES += ['collections.abc'] DEFAULT_INCLUDES = [] DEFAULT_BIN_EXCLUDES = ['MSVCP100.dll', 'MSVCP90.dll', 'w9xpopen.exe', 'MSVCP80.dll', 'MSVCR80.dll'] DEFAULT_BIN_INCLUDES = [] DEFAULT_BIN_PATH_INCLUDES = [] DEFAULT_BIN_PATH_EXCLUDES = [] def __init__(self): self.name = None self.version = None self.description = None self.target_name = None self._target_dir = None self.icon = None self.data_files = [] self.includes = self.DEFAULT_INCLUDES self.excludes = self.DEFAULT_EXCLUDES self.bin_includes = self.DEFAULT_BIN_INCLUDES self.bin_excludes = self.DEFAULT_BIN_EXCLUDES self.bin_path_includes = self.DEFAULT_BIN_PATH_INCLUDES self.bin_path_excludes = self.DEFAULT_BIN_PATH_EXCLUDES self.msvc = os.name == 'nt' self._py2exe_is_loaded = False self._pyqt_added = False self._pyside_added = False # Attributes relative to cx_Freeze: self.executables = [] @property def target_dir(self): """Return target directory (default: 'dist')""" dirname = self._target_dir if dirname is None: return 'dist' else: return dirname @target_dir.setter # analysis:ignore def target_dir(self, value): self._target_dir = value def setup(self, name, version, description, script, target_name=None, target_dir=None, icon=None, data_files=None, includes=None, excludes=None, bin_includes=None, bin_excludes=None, bin_path_includes=None, bin_path_excludes=None, msvc=None): """Setup distribution object Notes: * bin_path_excludes is specific to cx_Freeze (ignored if it's None) * if msvc is None, it's set to True by default on Windows platforms, False on non-Windows platforms """ self.name = name self.version = strip_version(version) if os.name == 'nt' else version self.description = description assert osp.isfile(script) self.script = script self.target_name = target_name self.target_dir = target_dir self.icon = icon if data_files is not None: self.data_files += data_files if includes is not None: self.includes += includes if excludes is not None: self.excludes += excludes if bin_includes is not None: self.bin_includes += bin_includes if bin_excludes is not None: self.bin_excludes += bin_excludes if bin_path_includes is not None: self.bin_path_includes += bin_path_includes if bin_path_excludes is not None: self.bin_path_excludes += bin_path_excludes if msvc is not None: self.msvc = msvc if self.msvc: try: self.data_files += create_msvc_data_files() except IOError: print("Setting the msvc option to False "\ "will avoid this error", file=sys.stderr) raise # cx_Freeze: self.add_executable(self.script, self.target_name, icon=self.icon) def add_text_data_file(self, filename, contents): """Create temporary data file *filename* with *contents* and add it to *data_files*""" open(filename, 'wb').write(to_binary_string(contents)) self.data_files += [("", (filename, ))] _remove_later(filename) def add_data_file(self, filename, destdir=''): self.data_files += [(destdir, (filename, ))] #------ Adding packages def add_pyqt(self): """Include module PyQt4 or PyQt5 to the distribution""" if self._pyqt_added: return self._pyqt_added = True try: import PyQt4 as PyQt qtver = 4 except ImportError: import PyQt5 as PyQt qtver = 5 self.includes += ['sip', 'PyQt%d.Qt' % qtver, 'PyQt%d.QtSvg' % qtver, 'PyQt%d.QtNetwork' % qtver] pyqt_path = osp.dirname(PyQt.__file__) # Configuring PyQt conf = os.linesep.join(["[Paths]", "Prefix = .", "Binaries = ."]) self.add_text_data_file('qt.conf', conf) # Including plugins (.svg icons support, QtDesigner support, ...) if self.msvc: vc90man = "Microsoft.VC90.CRT.manifest" pyqt_tmp = 'pyqt_tmp' if osp.isdir(pyqt_tmp): shutil.rmtree(pyqt_tmp) os.mkdir(pyqt_tmp) vc90man_pyqt = osp.join(pyqt_tmp, vc90man) if osp.isfile(vc90man): man = open(vc90man, "r").read().replace(', 2009. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: 2011-05-06 10:58+Paris, Madrid\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: utf-8\n" "Generated-By: pygettext.py 1.5\n" #: guidata\dataset\dataitems.py:38 msgid "float" msgstr "flottant" #: guidata\dataset\dataitems.py:38 msgid "integer" msgstr "entier" #: guidata\dataset\dataitems.py:44 msgid " and " msgstr " et " #: guidata\dataset\dataitems.py:44 msgid " between " msgstr " compris entre " #: guidata\dataset\dataitems.py:46 msgid " higher than " msgstr " supérieur à " #: guidata\dataset\dataitems.py:48 msgid " lower than " msgstr " inférieur à " #: guidata\dataset\dataitems.py:50 msgid "non zero" msgstr "non nul" #: guidata\dataset\dataitems.py:52 msgid "unit:" msgstr "unité :" #: guidata\dataset\dataitems.py:123 msgid "even" msgstr "pair" #: guidata\dataset\dataitems.py:125 msgid "odd" msgstr "impair" #: guidata\dataset\dataitems.py:244 msgid "all file types" msgstr "tout type de fichier" #: guidata\dataset\dataitems.py:245 msgid "supported file types:" msgstr "types de fichiers pris en charge : " #: guidata\dataset\qtitemwidgets.py:225 guidata\dataset\qtitemwidgets.py:279 msgid "Value is forced to %d" msgstr "La valeur est imposée à %d" #: guidata\dataset\qtitemwidgets.py:493 msgid "%s files" msgstr "Fichiers %s" #: guidata\dataset\qtitemwidgets.py:495 msgid "All supported files" msgstr "Tous les fichiers pris en charge" #: guidata\dataset\qtitemwidgets.py:655 msgid "Number of rows x Number of columns" msgstr "Nombre de lignes x Nombre de colonnes" #: guidata\dataset\qtitemwidgets.py:657 msgid "Edit array contents" msgstr "Modifier le contenu du tableau" #: guidata\dataset\qtitemwidgets.py:663 msgid "Smallest element in array" msgstr "Valeur minimale du tableau" #: guidata\dataset\qtitemwidgets.py:666 msgid "Largest element in array" msgstr "Valeur maximale du tableau" #: guidata\dataset\qtwidgets.py:123 msgid "Some required entries are incorrect" msgstr "Les champs surlignés n'ont pas été remplis correctement." #: guidata\dataset\qtwidgets.py:124 msgid "Please check highlighted fields." msgstr "Veuillez vérifier votre saisie." #: guidata\dataset\qtwidgets.py:485 msgid "Apply" msgstr "Appliquer" #: guidata\guitest.py:54 msgid "No description available" msgstr "Aucune description disponible" #: guidata\guitest.py:82 msgid "Description" msgstr "Description" #: guidata\guitest.py:92 msgid "Source code" msgstr "Code source" #: guidata\guitest.py:98 msgid "Run this script" msgstr "Exécuter ce script" #: guidata\guitest.py:100 msgid "Run all tests" msgstr "Exécuter tous les tests" #: guidata\guitest.py:101 msgid "Quit" msgstr "Quitter" #: guidata\guitest.py:123 msgid "Tests - %s module" msgstr "Tests - Module %s" #: guidata\qthelpers.py:211 msgid "Open a file" msgstr "Ouvrir un fichier" #: guidata\qthelpers.py:212 msgid "All" msgstr "Tout" #: guidata\qthelpers.py:214 msgid "Opening " msgstr "Ouverture de " #~ msgid "Array editor" #~ msgstr "Éditeur de tableaux" #~ msgid "" #~ "Resizing cells of a table of such size could take a long time.\n" #~ "Do you want to continue anyway?" #~ msgstr "" #~ "Redimensionner les cellules d'un tableau d'une telle taille sera " #~ "probablement très long.\n" #~ "Souhaitez-vous néanmoins effectuer cette opération ?" #~ msgid "Copy" #~ msgstr "Copier" #~ msgid "Format" #~ msgstr "Format" #~ msgid "Resize" #~ msgstr "Ajuster" #~ msgid "Background color" #~ msgstr "Couleur de fond" #~ msgid "Float formatting" #~ msgstr "Format de flottant" #~ msgid "Error" #~ msgstr "Erreur" #~ msgid "Format (%s) is incorrect" #~ msgstr "Le format (%s) est incorrect" #~ msgid "Array is empty" #~ msgstr "Le tableau est vide" #~ msgid "Arrays with more than 2 dimensions are not supported" #~ msgstr "" #~ "Les tableaux ayant plus de deux dimensions ne sont pas pris en charge" #~ msgid "The 'xlabels' argument length do no match array column number" #~ msgstr "" #~ "La taille de l'argument 'xlabels' ne correspond pas au nombre de colonnes " #~ "du tableau" #~ msgid "The 'ylabels' argument length do no match array row number" #~ msgstr "" #~ "La taille de l'argument 'ylabels' ne correspond pas au nombre de lignes " #~ "du tableau" #~ msgid "%s arrays" #~ msgstr "les tableaux %s" #~ msgid "%s are currently not supported" #~ msgstr "Attention: %s ne sont pas pris en charge" #~ msgid "read only" #~ msgstr "lecture seule" #~ msgid "Record array fields:" #~ msgstr "Champs :" #~ msgid "unknown" #~ msgstr "inconnue" #~ msgid "Index" #~ msgstr "Indice" #~ msgid "Name" #~ msgstr "Nom" #~ msgid "Tuple" #~ msgstr "Tuple" #~ msgid "List" #~ msgstr "Liste" #~ msgid "Dictionary" #~ msgstr "Dictionnaire" #~ msgid "Key" #~ msgstr "Clé" #~ msgid "elements" #~ msgstr "éléments" #~ msgid "Size" #~ msgstr "Taille" #~ msgid "Type" #~ msgstr "Type" #~ msgid "Value" #~ msgstr "Valeur" #~ msgid "Edit item" #~ msgstr "Modifier" #~ msgid "Unable to retrieve data.

Error message:
%s" #~ msgstr "" #~ "Impossible d'afficher les données

Message d'erreur :
%s" #~ msgid "Unable to assign data to item.

Error message:
%s" #~ msgstr "" #~ "Impossible d'accéder aux données

Message d'erreur :
%s" #~ msgid "Paste" #~ msgstr "Coller" #~ msgid "Edit" #~ msgstr "Modifier" #~ msgid "Plot" #~ msgstr "Tracer" #~ msgid "Show image" #~ msgstr "Afficher l'image" #~ msgid "Save array" #~ msgstr "Enregistrer le tableau" #~ msgid "Insert" #~ msgstr "Insérer" #~ msgid "Remove" #~ msgstr "Supprimer" #~ msgid "Truncate values" #~ msgstr "Tronquer les valeurs" #~ msgid "Show arrays min/max" #~ msgstr "Afficher les min/max des tableaux" #~ msgid "Show collection contents" #~ msgstr "Afficher le contenu des séquences" #~ msgid "Always edit in-place" #~ msgstr "Édition en ligne pour tous les types" #~ msgid "Rename" #~ msgstr "Renommer" #~ msgid "Duplicate" #~ msgstr "Dupliquer" #~ msgid "Do you want to remove selected item?" #~ msgstr "Souhaitez-vous supprimer l'élément sélectionné ?" #~ msgid "Do you want to remove all selected items?" #~ msgstr "Souhaitez-vous supprimer les éléments sélectionnés ?" #~ msgid "Key:" #~ msgstr "Clé :" #~ msgid "Value:" #~ msgstr "Valeur :" #~ msgid "Import error" #~ msgstr "Erreur d'import" #~ msgid "Please install guiqwt." #~ msgstr "Merci d'installer guiqwt." #~ msgid "Unable to plot data.

Error message:
%s" #~ msgstr "" #~ "Impossible d'afficher les données

Message d'erreur :
%s" #~ msgid "Unable to show image.

Error message:
%s" #~ msgstr "" #~ "Impossible d'afficher l'image

Message d'erreur :
%s" #~ msgid "NumPy arrays" #~ msgstr "Tableaux NumPy" #~ msgid "Unable to save array

Error message:
%s" #~ msgstr "" #~ "Impossible d'enregistrer le tableau

Message d'erreur :
%s" #~ msgid "Clipboard contents" #~ msgstr "Contenu du presse-papiers" #~ msgid "Import from clipboard" #~ msgstr "Importer depuis le presse-papiers" #~ msgid "Empty clipboard" #~ msgstr "Presse-papiers vide" #~ msgid "Nothing to be imported from clipboard." #~ msgstr "Aucune donnée ne peut être importée depuis le presse-papiers." #~ msgid "Import as" #~ msgstr "Importer en tant que" #~ msgid "data" #~ msgstr "données" #~ msgid "code" #~ msgstr "code" #~ msgid "text" #~ msgstr "texte" #~ msgid "Column separator:" #~ msgstr "Séparateur de colonne :" #~ msgid "Tab" #~ msgstr "Tab" #~ msgid "other" #~ msgstr "autre" #~ msgid "Row separator:" #~ msgstr "Séparateur de ligne :" #~ msgid "EOL" #~ msgstr "EOL" #~ msgid "Additionnal options" #~ msgstr "Options supplémentaires" #~ msgid "Skip rows:" #~ msgstr "Sauter des lignes :" #~ msgid "Comments:" #~ msgstr "Commentaires :" #~ msgid "Transpose" #~ msgstr "Transposer" #~ msgid "Import as array" #~ msgstr "Importer en tant que tableau" #~ msgid "Import wizard" #~ msgstr "Assistant d'importation" #~ msgid "Raw text" #~ msgstr "Text brut" #~ msgid "variable_name" #~ msgstr "nom_de_variable" #~ msgid "table" #~ msgstr "tableau" #~ msgid "Preview" #~ msgstr "Aperçu" #~ msgid "Cancel" #~ msgstr "Annuler" #~ msgid "Previous" #~ msgstr "Précédent" #~ msgid "Next" #~ msgstr "Suivant" #~ msgid "Done" #~ msgstr "Terminer" #~ msgid "" #~ "Unable to proceed to next step

Please check your entries." #~ "

Error message:
%s" #~ msgstr "" #~ "Impossible de passer à l'étape suivante

Merci de vérifier " #~ "votre saisie.

Message d'erreur :
%s" #~ msgid "Text editor" #~ msgstr "Éditeur de texte" #~ msgid "Progression" #~ msgstr "Progression" guidata-1.7.4/guidata/locale/fr/LC_MESSAGES/guidata.mo0000666000000000000000000000367312601727200020675 0ustar rootroot!$,- 3 = KXaey "  * /=M#g G RWgw     %'M _ m; &8Ujox# and between higher than lower than %s filesAllAll supported filesApplyDescriptionEdit array contentsLargest element in arrayNo description availableNumber of rows x Number of columnsOpen a fileOpening Please check highlighted fields.QuitRun all testsRun this scriptSmallest element in arraySome required entries are incorrectSource codeTests - %s moduleValue is forced to %dall file typesevenfloatintegernon zerooddsupported file types:unit:Project-Id-Version: PACKAGE VERSION POT-Creation-Date: 2011-05-06 10:58+Paris, Madrid PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: utf-8 Generated-By: pygettext.py 1.5 et compris entre supérieur à inférieur à Fichiers %sToutTous les fichiers pris en chargeAppliquerDescriptionModifier le contenu du tableauValeur maximale du tableauAucune description disponibleNombre de lignes x Nombre de colonnesOuvrir un fichierOuverture de Veuillez vérifier votre saisie.QuitterExécuter tous les testsExécuter ce scriptValeur minimale du tableauLes champs surlignés n'ont pas été remplis correctement.Code sourceTests - Module %sLa valeur est imposée à %dtout type de fichierpairflottantentiernon nulimpairtypes de fichiers pris en charge : unité :guidata-1.7.4/guidata/dataset/0000755000000000000000000000000012632514315014704 5ustar rootrootguidata-1.7.4/guidata/dataset/__init__.py0000666000000000000000000000114312601727200017013 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ dataset ======= The ``guidata.dataset`` package provides the core features for data set display and editing with automatically generated graphical user interfaces. .. automodule:: guidata.dataset.dataitems :members: .. automodule:: guidata.dataset.datatypes :members: .. automodule:: guidata.dataset.qtitemwidgets :members: .. automodule:: guidata.dataset.qtwidgets :members: """ guidata-1.7.4/guidata/dataset/qtwidgets.py0000666000000000000000000004636712611637764017330 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ dataset.qtwidgets ================= Dialog boxes used to edit data sets: DataSetEditDialog DataSetGroupEditDialog DataSetShowDialog ...and layouts: GroupItem DataSetEditLayout DataSetShowLayout """ from __future__ import print_function try: # PyQt4 4.3.3 on Windows (static DLLs) with py2exe installed: # -> pythoncom must be imported first, otherwise py2exe's boot_com_servers # will raise an exception ("Unable to load DLL [...]") when calling any # of the QFileDialog static methods (getOpenFileName, ...) import pythoncom except ImportError: pass from guidata.qt.QtGui import (QDialog, QMessageBox, QDialogButtonBox, QWidget, QVBoxLayout, QGridLayout, QLabel, QSpacerItem, QColor, QTabWidget, QIcon, QApplication, QPainter, QPicture, QBrush, QGroupBox, QPushButton) from guidata.qt.QtCore import Qt, QRect, QSize, Signal from guidata.qt.compat import getopenfilename, getopenfilenames, getsavefilename from guidata.configtools import get_icon from guidata.config import _ from guidata.py3compat import to_text_string, is_text_string from guidata.dataset.datatypes import (BeginGroup, EndGroup, GroupItem, TabGroupItem) class DataSetEditDialog(QDialog): """ Dialog box for DataSet editing """ def __init__(self, instance, icon='', parent=None, apply=None, wordwrap=True, size=None): QDialog.__init__(self, parent) self.wordwrap = wordwrap self.apply_func = apply self.layout = QVBoxLayout() if instance.get_comment(): label = QLabel(instance.get_comment()) label.setWordWrap(wordwrap) self.layout.addWidget(label) self.instance = instance self.edit_layout = [ ] self.setup_instance( instance ) if apply is not None: apply_button = QDialogButtonBox.Apply else: apply_button = QDialogButtonBox.NoButton bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel | apply_button ) self.bbox = bbox bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) bbox.clicked.connect(self.button_clicked) self.layout.addWidget(bbox) self.setLayout(self.layout) if parent is None: if not isinstance(icon, QIcon): icon = get_icon(icon, default="guidata.svg") self.setWindowIcon(icon) self.setModal(True) self.setWindowTitle(instance.get_title()) if size is not None: if isinstance(size, QSize): self.resize(size) else: self.resize(*size) def button_clicked(self, button): role = self.bbox.buttonRole(button) if role==QDialogButtonBox.ApplyRole and self.apply_func is not None: if self.check(): for edl in self.edit_layout: edl.accept_changes() self.apply_func(self.instance) def setup_instance(self, instance): """Construct main layout""" grid = QGridLayout() grid.setAlignment(Qt.AlignTop) self.layout.addLayout(grid) self.edit_layout.append( self.layout_factory( instance, grid) ) def layout_factory(self, instance, grid ): """A factory method that produces instances of DataSetEditLayout or derived classes (see DataSetShowDialog) """ return DataSetEditLayout( self, instance, grid ) def child_title(self, item): """Return data item title combined with QApplication title""" app_name = QApplication.applicationName() if not app_name: app_name = self.instance.get_title() return "%s - %s" % ( app_name, item.label() ) def check(self): is_ok = True for edl in self.edit_layout: if not edl.check_all_values(): is_ok = False if not is_ok: QMessageBox.warning(self, self.instance.get_title(), _("Some required entries are incorrect")+"\n"+\ _("Please check highlighted fields.")) return False return True def accept(self): """Validate inputs""" if self.check(): for edl in self.edit_layout: edl.accept_changes() QDialog.accept(self) class DataSetGroupEditDialog(DataSetEditDialog): """ Tabbed dialog box for DataSet editing """ def setup_instance(self, instance): """Override DataSetEditDialog method""" from guidata.dataset.datatypes import DataSetGroup assert isinstance(instance, DataSetGroup) tabs = QTabWidget() # tabs.setUsesScrollButtons(False) self.layout.addWidget(tabs) for dataset in instance.datasets: layout = QVBoxLayout() layout.setAlignment(Qt.AlignTop) if dataset.get_comment(): label = QLabel(dataset.get_comment()) label.setWordWrap(self.wordwrap) layout.addWidget(label) grid = QGridLayout() self.edit_layout.append( self.layout_factory(dataset, grid) ) layout.addLayout(grid) page = QWidget() page.setLayout(layout) if dataset.get_icon(): tabs.addTab( page, get_icon(dataset.get_icon()), dataset.get_title() ) else: tabs.addTab( page, dataset.get_title() ) class DataSetEditLayout(object): """ Layout in which data item widgets are placed """ _widget_factory = {} @classmethod def register(cls, item_type, factory): """Register a factory for a new item_type""" cls._widget_factory[item_type] = factory def __init__(self, parent, instance, layout, items=None, first_line=0): self.parent = parent self.instance = instance self.layout = layout self.first_line = first_line self.widgets = [] self.linenos = {} # prochaine ligne à remplir par colonne self.items_pos = {} if not items: items = self.instance._items items = self.transform_items( items ) self.setup_layout( items ) def transform_items(self, items): """ Handle group of items: transform items into a GroupItem instance if they are located between BeginGroup and EndGroup """ item_lists = [ [] ] for item in items: if isinstance(item, BeginGroup): item = item.get_group() item_lists[-1].append(item) item_lists.append( item.group ) elif isinstance(item, EndGroup): item_lists.pop() else: item_lists[-1].append(item) assert len(item_lists)==1 return item_lists[-1] def check_all_values(self): """Check input of all widgets""" for widget in self.widgets: if widget.is_active() and not widget.check(): return False return True def accept_changes(self): """Accept changes made to widget inputs""" self.update_dataitems() def setup_layout(self, items): """Place items on layout""" def last_col(col, span): """Return last column (which depends on column span)""" if not span: return col else: return col+span-1 colmax = max( [ last_col(item.get_prop("display", "col"), item.get_prop("display", "colspan")) for item in items ] ) self.items_pos = {} line = self.first_line - 1 last_item = [-1, 0, colmax ] for item in items: beg = item.get_prop("display", "col") span = item.get_prop("display", "colspan") if span is None: span = colmax - beg + 1 if beg <= last_item[1]: # on passe à la ligne si la colonne de debut de cet item # est avant la colonne de debut de l'item précédent line += 1 else: last_item[2] = beg-last_item[1] last_item = [line, beg, span] self.items_pos[item] = last_item for item in items: hide = item.get_prop_value("display", self.instance, "hide", False) if hide: continue widget = self.build_widget(item) self.add_row( widget ) self.refresh_widgets() def build_widget(self, item): factory = self._widget_factory[type(item)] widget = factory( item.bind(self.instance), self ) self.widgets.append( widget ) return widget def add_row(self, widget): """Add widget to row""" item = widget.item line, col, span = self.items_pos[item.item] if col > 0: self.layout.addItem( QSpacerItem(20, 1), line, col*3-1 ) widget.place_on_grid( self.layout, line, col*3, col*3 + 1, 1, 3*span-2) try: widget.get() except Exception: print("Error building item :", item.item._name) raise def refresh_widgets(self): """Refresh the status of all widgets""" for widget in self.widgets: widget.set_state() def update_dataitems(self): """Refresh the content of all data items""" for widget in self.widgets: if widget.is_active(): widget.set() def update_widgets(self, except_this_one=None): """Refresh the content of all widgets""" for widget in self.widgets: if widget is not except_this_one: widget.get() # Enregistrement des correspondances avec les widgets from guidata.dataset.qtitemwidgets import (LineEditWidget, TextEditWidget, CheckBoxWidget, ColorWidget, FileWidget, DirectoryWidget, ChoiceWidget, MultipleChoiceWidget, FloatArrayWidget, GroupWidget, AbstractDataSetWidget, ButtonWidget, TabGroupWidget, DateWidget, DateTimeWidget, SliderWidget, FloatSliderWidget) from guidata.dataset.dataitems import (FloatItem, StringItem, TextItem, IntItem, BoolItem, ColorItem, FileOpenItem, FilesOpenItem, FileSaveItem, DirectoryItem, ChoiceItem, ImageChoiceItem, MultipleChoiceItem, FloatArrayItem, ButtonItem, DateItem, DateTimeItem, DictItem) DataSetEditLayout.register(GroupItem, GroupWidget) DataSetEditLayout.register(TabGroupItem, TabGroupWidget) DataSetEditLayout.register(FloatItem, LineEditWidget) DataSetEditLayout.register(StringItem, LineEditWidget) DataSetEditLayout.register(TextItem, TextEditWidget) DataSetEditLayout.register(IntItem, SliderWidget) DataSetEditLayout.register(FloatItem, FloatSliderWidget) DataSetEditLayout.register(BoolItem, CheckBoxWidget) DataSetEditLayout.register(DateItem, DateWidget) DataSetEditLayout.register(DateTimeItem, DateTimeWidget) DataSetEditLayout.register(ColorItem, ColorWidget) DataSetEditLayout.register(FileOpenItem, lambda item, parent: FileWidget(item, parent, getopenfilename) ) DataSetEditLayout.register(FilesOpenItem, lambda item, parent: FileWidget(item, parent, getopenfilenames) ) DataSetEditLayout.register(FileSaveItem, lambda item, parent: FileWidget(item, parent, getsavefilename) ) DataSetEditLayout.register(DirectoryItem, DirectoryWidget) DataSetEditLayout.register(ChoiceItem, ChoiceWidget) DataSetEditLayout.register(ImageChoiceItem, ChoiceWidget) DataSetEditLayout.register(MultipleChoiceItem, MultipleChoiceWidget) DataSetEditLayout.register(FloatArrayItem, FloatArrayWidget) DataSetEditLayout.register(ButtonItem, ButtonWidget) DataSetEditLayout.register(DictItem, ButtonWidget) LABEL_CSS = """ QLabel { font-weight: bold; color: blue } QLabel:disabled { font-weight: bold; color: grey } """ class DataSetShowWidget(AbstractDataSetWidget): """Read-only base widget""" READ_ONLY = True def __init__(self, item, parent_layout): AbstractDataSetWidget.__init__(self, item, parent_layout) self.group = QLabel() wordwrap = item.get_prop_value("display", "wordwrap", False) self.group.setWordWrap(wordwrap) self.group.setToolTip(item.get_help()) self.group.setStyleSheet( LABEL_CSS ) self.group.setTextInteractionFlags(Qt.TextSelectableByMouse) #self.group.setEnabled(False) def get(self): """Override AbstractDataSetWidget method""" self.set_state() text = self.item.get_string_value() self.group.setText(text) def set(self): """Read only...""" pass class ShowColorWidget(DataSetShowWidget): """Read-only color item widget""" def __init__(self, item, parent_layout): DataSetShowWidget.__init__(self, item, parent_layout) self.picture = None def get(self): """Override AbstractDataSetWidget method""" value = self.item.get() if value is not None: color = QColor(value) self.picture = QPicture() painter = QPainter() painter.begin(self.picture) painter.fillRect(QRect(0, 0, 60, 20), QBrush(color)) painter.end() self.group.setPicture(self.picture) class ShowBooleanWidget(DataSetShowWidget): """Read-only bool item widget""" def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" if not self.item.get_prop_value("display", "label"): widget_column = label_column column_span += 1 else: self.place_label(layout, row, label_column) layout.addWidget(self.group, row, widget_column, row_span, column_span) def get(self): """Override AbstractDataSetWidget method""" DataSetShowWidget.get(self) text = self.item.get_prop_value("display", "text") self.group.setText(text) font = self.group.font() value = self.item.get() state = bool(value) font.setStrikeOut(not state) self.group.setFont(font) self.group.setEnabled(state) class DataSetShowLayout(DataSetEditLayout): """Read-only layout""" _widget_factory = {} class DataSetShowDialog(DataSetEditDialog): """Read-only dialog box""" def layout_factory(self, instance, grid ): """Override DataSetEditDialog method""" return DataSetShowLayout( self, instance, grid ) DataSetShowLayout.register(GroupItem, GroupWidget) DataSetShowLayout.register(TabGroupItem, TabGroupWidget) DataSetShowLayout.register(FloatItem, DataSetShowWidget) DataSetShowLayout.register(StringItem, DataSetShowWidget) DataSetShowLayout.register(TextItem, DataSetShowWidget) DataSetShowLayout.register(IntItem, DataSetShowWidget) DataSetShowLayout.register(BoolItem, ShowBooleanWidget) DataSetShowLayout.register(DateItem, DataSetShowWidget) DataSetShowLayout.register(DateTimeItem, DataSetShowWidget) DataSetShowLayout.register(ColorItem, ShowColorWidget) DataSetShowLayout.register(FileOpenItem, DataSetShowWidget ) DataSetShowLayout.register(FilesOpenItem, DataSetShowWidget ) DataSetShowLayout.register(FileSaveItem, DataSetShowWidget ) DataSetShowLayout.register(DirectoryItem, DataSetShowWidget) DataSetShowLayout.register(ChoiceItem, DataSetShowWidget) DataSetShowLayout.register(ImageChoiceItem, DataSetShowWidget) DataSetShowLayout.register(MultipleChoiceItem, DataSetShowWidget) DataSetShowLayout.register(FloatArrayItem, DataSetShowWidget) class DataSetShowGroupBox(QGroupBox): """Group box widget showing a read-only DataSet""" def __init__(self, label, klass, wordwrap=False, **kwargs): QGroupBox.__init__(self, label) self.klass = klass self.dataset = klass(**kwargs) self.layout = QVBoxLayout() if self.dataset.get_comment(): label = QLabel(self.dataset.get_comment()) label.setWordWrap(wordwrap) self.layout.addWidget(label) self.grid_layout = QGridLayout() self.layout.addLayout(self.grid_layout) self.setLayout(self.layout) self.edit = self.get_edit_layout() def get_edit_layout(self): """Return edit layout""" return DataSetShowLayout(self, self.dataset, self.grid_layout) def get(self): """Update group box contents from data item values""" for widget in self.edit.widgets: widget.build_mode = True widget.get() widget.build_mode = False class DataSetEditGroupBox(DataSetShowGroupBox): """ Group box widget including a DataSet label: group box label (string) klass: guidata.DataSet class button_text: action button text (default: "Apply") button_icon: QIcon object or string (default "apply.png") """ #: Signal emitted when Apply button is clicked SIG_APPLY_BUTTON_CLICKED = Signal() def __init__(self, label, klass, button_text=None, button_icon=None, show_button=True, wordwrap=False, **kwargs): DataSetShowGroupBox.__init__(self, label, klass, wordwrap=wordwrap, **kwargs) if show_button: if button_text is None: button_text = _("Apply") if button_icon is None: button_icon = get_icon("apply.png") elif is_text_string(button_icon): button_icon = get_icon(button_icon) apply_btn = QPushButton(button_icon, button_text, self) apply_btn.clicked.connect(self.set) layout = self.edit.layout layout.addWidget(apply_btn, layout.rowCount(), 0, 1, -1, Qt.AlignRight) def get_edit_layout(self): """Return edit layout""" return DataSetEditLayout(self, self.dataset, self.grid_layout) def set(self): """Update data item values from layout contents""" for widget in self.edit.widgets: if widget.is_active() and widget.check(): widget.set() self.SIG_APPLY_BUTTON_CLICKED.emit() def child_title(self, item): """Return data item title combined with QApplication title""" app_name = QApplication.applicationName() if not app_name: app_name = to_text_string(self.title()) return "%s - %s" % ( app_name, item.label() ) guidata-1.7.4/guidata/dataset/datatypes.py0000666000000000000000000007360412611427762017300 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ dataset.datatypes ================= The ``guidata.dataset.datatypes`` module contains implementation for DataSets (DataSet, DataSetGroup, ...) and related objects (ItemProperty, ValueProp, ...). """ # pylint: disable-msg=W0622 # pylint: disable-msg=W0212 from __future__ import print_function, unicode_literals import sys import re import collections from guidata.utils import utf8_to_unicode, update_dataset from guidata.py3compat import to_text_string, is_text_string, PY2 DEBUG_DESERIALIZE = False class NoDefault: pass class ItemProperty(object): def __init__(self, callable=None): self.callable=callable def __call__(self, instance, item, value): """Evaluate the value of the property given, the instance, the item and the value maintained in the instance by the item""" return self.callable(instance, item, value) def set(self, instance, item, value): """Sets the value of the property given an instance, item and value Depending on implementation the value will be stored either on the instance, item or self """ raise NotImplementedError FMT_GROUPS=re.compile(r"(?dict containing realm-specific properties self.set_prop("display", col=0, colspan=None, label=utf8_to_unicode(label)) self.set_prop('data', check_value=check) def get_prop(self, realm, name, default=NoDefault): """Get one property of this item""" prop = self._props.get(realm) if not prop: prop = {} if default is NoDefault: return prop[name] return prop.get(name, default) def get_prop_value(self, realm, instance, name, default=NoDefault): value = self.get_prop(realm, name, default) if isinstance(value, ItemProperty): return value(instance, self, self.get_value(instance)) else: return value def set_prop(self, realm, **kwargs): """Set one or several properties using the syntax set_prop(name1=value1, ..., nameX=valueX) it returns self so that we can assign to the result like this: item = Item().set_prop(x=y) """ prop = self._props.get(realm) if not prop: prop = {} self._props[realm] = prop prop.update(kwargs) return self def set_pos(self, col=0, colspan=None): """ Set data item's position on a GUI layout """ self.set_prop("display", col=col, colspan=colspan) return self def __str__(self): return self._name + ": " + self.__class__.__name__ def get_help(self, instance): """ Return data item's tooltip """ auto_help = utf8_to_unicode(self.get_auto_help(instance)) help = self._help if auto_help: if help: help = help + "\n(" + auto_help + ")" else: help = auto_help.capitalize() return help def get_auto_help(self, instance): """ Return the automatically generated part of data item's tooltip """ return "" def format_string(self, instance, value, fmt, func): """Apply format to string representation of the item's value""" return fmt % (func(value), ) def get_string_value(self, instance): """ Return a formatted unicode representation of the item's value obeying 'display' or 'repr' properties """ value = self.get_value(instance) repval = self.get_prop_value("display", instance, "repr", None) if repval is not None: return repval else: fmt = self.get_prop_value("display", instance, "format", "%s") func = self.get_prop_value("display", instance, "func", lambda x:x) if isinstance(fmt, collections.Callable) and value is not None: return fmt(func(value)) elif is_text_string(fmt): # This is only necessary with Python 2: converting to unicode fmt = to_text_string(fmt) if value is not None: text = self.format_string(instance, value, fmt, func) else: text = "" return text def set_name(self, new_name): """ Set data item's name """ self._name = new_name def set_from_string(self, instance, string_value): """ Set data item's value from specified string """ value = self.from_string(string_value) self.__set__(instance, value) def set_default(self, instance): """ Set data item's value to default """ self.__set__(instance, self._default) def accept(self, visitor): """ This is the visitor pattern's accept function. It calls the corresponding visit_MYCLASS method of the visitor object. Python's allow a generic base class implementation of this method so there's no need to write an accept function for each derived class unless you need to override the default behavior """ funcname = "visit_"+self.__class__.__name__ func = getattr(visitor, funcname) func(self) def __set__(self, instance, value): setattr(instance, "_"+self._name, value) def __get__(self, instance, klass): if instance is not None: return getattr(instance, "_"+self._name, self._default) else: return self def get_value(self, instance): """ Return data item's value """ return self.__get__(instance, instance.__class__) def check_item(self, instance): """ Check data item's current value (calling method check_value) """ value = getattr(instance, "_"+self._name) return self.check_value(value) def check_value(self, instance, value): """ Check if `value` is valid for this data item """ raise NotImplementedError() def from_string(self, instance, string_value): """ Transform string into valid data item's value """ raise NotImplementedError() def bind(self, instance): """ Return a DataItemVariable instance bound to the data item """ return DataItemVariable(self, instance) def serialize(self, instance, writer): """Serialize this item using the writer object this is a default implementation that should work for everything but new datatypes """ value = self.get_value(instance) writer.write(value) def get_value_from_reader(self, reader): """Reads value from the reader object, inside the try...except statement defined in the base item `deserialize` method This method is reimplemented in some child classes""" return reader.read_any() def deserialize(self, instance, reader): """Deserialize this item using the reader object Default base implementation supposes the reader can detect expected datatype from the stream """ try: value = self.get_value_from_reader(reader) except RuntimeError as e: if DEBUG_DESERIALIZE: import traceback print("DEBUG_DESERIALIZE enabled in datatypes.py", file=sys.stderr) traceback.print_stack() print(e, file=sys.stderr) self.set_default(instance) return self.__set__(instance, value) class Obj(object): """An object that helps build default instances for ObjectItems""" def __init__(self, **kwargs): self.__dict__.update(kwargs) class ObjectItem(DataItem): """Simple helper class implementing default for composite objects""" klass = None def set_default(self, instance): """Make a copy of the default value """ value = self.klass() if self._default is not None: update_dataset(value, self._default) self.__set__(instance, value) def deserialize(self, instance, reader): """Deserialize this item using the reader object We build a new default object and deserialize it """ value = self.klass() value.deserialize(reader) self.__set__(instance, value) class DataItemProxy(object): """ Proxy for DataItem objects This class is needed to construct GroupItem class (see module guidata.qtwidgets) """ def __init__(self, item): self.item = item def __str__(self): return self.item._name + "_proxy: " + self.__class__.__name__ def get_help(self, instance): """DataItem method proxy""" return self.item.get_help(instance) def get_auto_help(self, instance): """DataItem method proxy""" return self.item.get_auto_help(instance) def get_string_value(self, instance): """DataItem method proxy""" return self.item.get_string_value(instance) def set_from_string(self, instance, string_value): """DataItem method proxy""" return self.item.set_from_string(instance, string_value) def set_default(self, instance): """DataItem method proxy""" return self.item.set_default(instance) def accept(self, visitor): """DataItem method proxy""" return self.item.accept(visitor) def get_value(self, instance): """DataItem method proxy""" return self.item.get_value(instance) def check_item(self, instance): """DataItem method proxy""" return self.item.check_item(instance) def check_value(self, instance, value): """DataItem method proxy""" return self.item.check_value(instance, value) def from_string(self, instance, string_value): """DataItem method proxy""" return self.item.from_string(instance, string_value) def get_prop(self, realm, name, default=NoDefault): """DataItem method proxy""" return self.item.get_prop(realm, name, default) def get_prop_value(self, realm, instance, name, default=NoDefault): """DataItem method proxy""" return self.item.get_prop_value(realm, instance, name, default) def set_prop(self, realm, **kwargs): """DataItem method proxy""" return self.item.set_prop(realm, **kwargs) def bind(self, instance): """DataItem method proxy""" return DataItemVariable(self, instance) # def __getattr__(self, name): # assert name in ["min_equals_max", "get_min", "get_max", # "_formats", "_text", "_choices", "_shape", # "_format", "_label", "_xy"] # val = getattr(self.item, name) # if callable(val): # return bind(val, self.instance) # else: # return val class DataItemVariable(object): """An instance of a DataItemVariable represent a binding between an item and a dataset. could be called a bound property. since DataItem instances are class attributes they need to have a DataSet instance to store their value. This class binds the two together. """ def __init__(self, item, instance): self.item = item self.instance = instance def get_prop_value(self, realm, name, default=NoDefault): """DataItem method proxy""" return self.item.get_prop_value(realm, self.instance, name, default) def get_prop(self, realm, name, default=NoDefault): """DataItem method proxy""" return self.item.get_prop(realm, name, default) # def set_prop(self, realm, **kwargs): # """DataItem method proxy""" # self.item.set_prop(realm, **kwargs) # # def __getattr__(self, name): # assert name in ["min_equals_max", "get_min", "get_max", # "_formats","_text", "_choices", "_shape", # "_format", "_label", "_xy"] # val = getattr(self.item, name) # if callable(val): # return bind(val, self.instance) # else: # return val def get_help(self): """Re-implement DataItem method""" return self.item.get_help(self.instance) def get_auto_help(self): """Re-implement DataItem method""" # XXX incohérent ? return self.item.get_auto_help(self.instance) def get_string_value(self): """ Return a unicode representation of the item's value obeying 'display' or 'repr' properties """ return self.item.get_string_value(self.instance) def set_default(self): """Re-implement DataItem method""" return self.item.set_default(self.instance) def get(self): """Re-implement DataItem method""" return self.item.get_value(self.instance) def set(self, value): """Re-implement DataItem method""" return self.item.__set__(self.instance, value) def set_from_string(self, string_value): """Re-implement DataItem method""" return self.item.set_from_string(self.instance, string_value) def check_item(self): """Re-implement DataItem method""" return self.item.check_item(self.instance) def check_value(self, value): """Re-implement DataItem method""" return self.item.check_value(value) def from_string(self, string_value): """Re-implement DataItem method""" return self.item.from_string(string_value) def label(self): """Re-implement DataItem method""" return self.item.get_prop("display", "label") class DataSetMeta(type): """ DataSet metaclass Create class attribute `_items`: list of the DataSet class attributes, created in the same order as these attributes were written """ def __new__(cls, name, bases, dct): items = {} for base in bases: if getattr(base, "__metaclass__", None) is DataSetMeta: for item in base._items: items[item._name] = item for attrname, value in list(dct.items()): if isinstance( value, DataItem ): value.set_name(attrname) if attrname in items: value._order = items[attrname]._order items[attrname] = value items_list = list(items.values()) items_list.sort(key=lambda x:x._order) dct["_items"] = items_list return type.__new__(cls, name, bases, dct) if PY2: Meta_Py3Compat = DataSetMeta(b'Meta_Py3Compat', (object, ), {}) else: Meta_Py3Compat = DataSetMeta('Meta_Py3Compat', (object, ), {}) class DataSet(Meta_Py3Compat): """ Construct a DataSet object is a set of DataItem objects * title [string] * comment [string]: text shown on the top of the first data item * icon [QIcon or string]: icon show on the button (optional) (string: icon filename as in guidata/guiqwt image search paths) """ __metaclass__ = DataSetMeta # keep it even with Python 3 (see DataSetMeta) def __init__(self, title=None, comment=None, icon=''): self.__title = title self.__comment = comment self.__icon = icon comp_title, comp_comment = self._compute_title_and_comment() if title is None: self.__title = comp_title if comment is None: self.__comment = comp_comment self.__changed = False # Set default values self.set_defaults() def _get_translation(self): """We try to find the translation function (_) from the module this class was created in This function is unused but could be useful to translate strings that cannot be translated at the time they are created. """ module = sys.modules[self.__class__.__module__] if hasattr(module, "_"): return module._ else: return lambda x:x def _compute_title_and_comment(self): """ Private method to compute title and comment of the data set """ comp_title = self.__class__.__name__ comp_comment = None if self.__doc__: doc_lines = utf8_to_unicode(self.__doc__).splitlines() # Remove empty lines at the begining of comment while doc_lines and not doc_lines[0].strip(): del doc_lines[0] if doc_lines: comp_title = doc_lines.pop(0).strip() if doc_lines: comp_comment = "\n".join([x.strip() for x in doc_lines]) return comp_title, comp_comment def get_title(self): """ Return data set title """ return self.__title def get_comment(self): """ Return data set comment """ return self.__comment def get_icon(self): """ Return data set icon """ return self.__icon def set_defaults(self): """Set default values""" for item in self._items: item.set_default(self) def __str__(self): return self.to_string(debug=False) def check(self): """ Check the dataset item values """ errors = [] for item in self._items: if not item.check_item(self): errors.append(item._name) return errors def text_edit(self): """ Edit data set with text input only """ from guidata.dataset import textedit self.accept(textedit.TextEditVisitor(self)) def edit(self, parent=None, apply=None, size=None): """ Open a dialog box to edit data set * parent: parent widget (default is None, meaning no parent) * apply: apply callback (default is None) * size: dialog size (QSize object or integer tuple (width, height)) """ from guidata.dataset.qtwidgets import DataSetEditDialog win = DataSetEditDialog(self, icon=self.__icon, parent=parent, apply=apply, size=size) return win.exec_() def view(self, parent=None, size=None): """ Open a dialog box to view data set * parent: parent widget (default is None, meaning no parent) * size: dialog size (QSize object or integer tuple (width, height)) """ from guidata.dataset.qtwidgets import DataSetShowDialog win = DataSetShowDialog(self, icon=self.__icon, parent=parent, size=size) return win.exec_() def to_string(self, debug=False, indent=None, align=False): """ Return readable string representation of the data set If debug is True, add more details on data items """ if indent is None: indent = "\n " txt = self.__title+":" def _get_label(item): if debug: return item._name else: return item.get_prop_value("display", self, "label") length = 0 if align: for item in self._items: item_length = len(_get_label(item)) if item_length > length: length = item_length for item in self._items: if isinstance(item, ObjectItem): composite_dataset = item.get_value(self) txt += indent+composite_dataset.to_string(debug=debug, indent=indent+" ") continue elif isinstance(item, BeginGroup): txt += indent+item._name+":" indent += " " continue elif isinstance(item, EndGroup): indent = indent[:-2] continue value = getattr(self, "_"+item._name) if value is None: value_str = "-" else: value_str = item.get_string_value(self) if debug: label = item._name else: label = item.get_prop_value("display", self, "label") if length: label = label.ljust(length) txt += indent+label+": "+value_str if debug: txt += " ("+item.__class__.__name__+")" return txt def accept(self, vis): """ helper function that passes the visitor to the accept methods of all the items in this dataset """ for item in self._items: item.accept(vis) def serialize(self, writer): for item in self._items: with writer.group(item._name): item.serialize(self, writer) def deserialize(self, reader): for item in self._items: with reader.group(item._name): try: item.deserialize(self, reader) except RuntimeError as error: if DEBUG_DESERIALIZE: import traceback print("DEBUG_DESERIALIZE enabled in datatypes.py", file=sys.stderr) traceback.print_stack() print(error, file=sys.stderr) item.set_default(self) def read_config(self, conf, section, option): from guidata.userconfigio import UserConfigReader reader = UserConfigReader(conf, section, option) self.deserialize(reader) def write_config(self, conf, section, option): from guidata.userconfigio import UserConfigWriter writer = UserConfigWriter(conf, section, option) self.serialize(writer) @classmethod def set_global_prop(klass, realm, **kwargs): for item in klass._items: item.set_prop(realm, **kwargs) class ActivableDataSet(DataSet): """ An ActivableDataSet instance must have an "enable" class attribute which will set the active state of the dataset instance (see example in: tests/activable_dataset.py) """ _ro = True # default *instance* attribute value _active = True _ro_prop = GetAttrProp("_ro") _active_prop = GetAttrProp("_active") def __init__(self, title=None, comment=None, icon=''): DataSet.__init__(self, title, comment, icon) # self.set_readonly() @classmethod def active_setup(klass): """ This class method must be called after the child class definition in order to setup the dataset active state """ klass.set_global_prop("display", active=klass._active_prop) klass.enable.set_prop("display", active=True, hide=klass._ro_prop, store=klass._active_prop) def set_readonly(self): """ The dataset is now in read-only mode, i.e. all data items are disabled """ self._ro = True self._active = self.enable def set_writeable(self): """ The dataset is now in read/write mode, i.e. all data items are enabled """ self._ro = False self._active = self.enable class DataSetGroup(object): """ Construct a DataSetGroup object, used to group several datasets together * datasets [list of DataSet objects] * title [string] * icon [QIcon or string]: icon show on the button (optional) (string: icon filename as in guidata/guiqwt image search paths) This class tries to mimics the DataSet interface. The GUI should represent it as a notebook with one page for each contained dataset. """ def __init__(self, datasets, title=None, icon=''): self.__icon = icon self.datasets = datasets if title: self.__title = title else: self.__title = self.__class__.__name__ def __str__(self): return "\n".join([dataset.__str__() for dataset in self.datasets]) def get_title(self): """ Return data set group title """ return self.__title def get_comment(self): """ Return data set group comment --> not implemented (will return None) """ return None def check(self): """ Check data set group items """ return [dataset.check() for dataset in self.datasets] def text_edit(self): """ Edit data set with text input only """ raise NotImplementedError() def edit(self, parent=None, apply=None): """ Open a dialog box to edit data set """ from guidata.dataset.qtwidgets import DataSetGroupEditDialog win = DataSetGroupEditDialog(self, icon=self.__icon, parent=parent, apply=apply) return win.exec_() def accept(self, vis): """ helper function that passes the visitor to the accept methods of all the items in this dataset """ for dataset in self.datasets: dataset.accept(vis) class GroupItem(DataItemProxy): """GroupItem proxy""" def __init__(self, item): DataItemProxy.__init__(self, item) self.group = [] class BeginGroup(DataItem): """ Data item which does not represent anything but a begin flag to define a data set group """ def serialize(self, instance, writer): pass def deserialize(self, instance, reader): pass def get_group(self): return GroupItem(self) class EndGroup(DataItem): """ Data item which does not represent anything but an end flag to define a data set group """ def serialize(self, instance, writer): pass def deserialize(self, instance, reader): pass class TabGroupItem(GroupItem): pass class BeginTabGroup(BeginGroup): def get_group(self): return TabGroupItem(self) class EndTabGroup(EndGroup): pass guidata-1.7.4/guidata/dataset/textedit.py0000666000000000000000000000166612601727200017120 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ Text visitor for DataItem objects (for test purpose only) """ from __future__ import print_function from guidata.py3compat import input def prompt(item): """Get item value""" return input(item.get_prop("display", "label")+" ? ") class TextEditVisitor: """Text visitor""" def __init__(self, instance): self.instance = instance def visit_generic(self, item): """Generic visitor""" while True: value = prompt(item) item.set_from_string(self.instance, value) if item.check_item(self.instance): break print("Incorrect value!") visit_FloatItem = visit_generic visit_IntItem = visit_generic visit_StringItem = visit_generic guidata-1.7.4/guidata/dataset/qtitemwidgets.py0000666000000000000000000011010012627662256020161 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ dataset.qtitemwidgets ===================== Widget factories used to edit data items (factory registration is done in guidata.dataset.qtwidgets) (data item types are defined in guidata.dataset.datatypes) There is one widget type for each data item type. Example: ChoiceWidget <--> ChoiceItem, ImageChoiceItem """ from __future__ import print_function, unicode_literals import os import os.path as osp import sys import numpy import collections import datetime try: # PyQt4 4.3.3 on Windows (static DLLs) with py2exe installed: # -> pythoncom must be imported first, otherwise py2exe's boot_com_servers # will raise an exception ("Unable to load DLL [...]") when calling any # of the QFileDialog static methods (getOpenFileName, ...) import pythoncom except ImportError: pass from guidata.qt.QtGui import (QIcon, QPixmap, QHBoxLayout, QGridLayout, QColor, QColorDialog, QPushButton, QLineEdit, QCheckBox, QComboBox, QTabWidget, QGroupBox, QDateTimeEdit, QLabel, QTextEdit, QFrame, QDateEdit, QSlider, QRadioButton, QVBoxLayout) from guidata.qt.QtCore import Qt from guidata.qt.compat import getexistingdirectory try: from guidata.qt.QtCore import QStringList except ImportError: # PyQt API#2 QStringList = list from guidata.utils import update_dataset, restore_dataset, utf8_to_unicode from guidata.qthelpers import text_to_qcolor, get_std_icon from guidata.configtools import get_icon, get_image_layout, get_image_file_path from guidata.config import _ from guidata.py3compat import to_text_string, is_text_string # ========================== IMPORTANT ================================= # # In this module, `item` is an instance of DataItemVariable (not DataItem) # (see guidata.datatypes for details) # # ========================== IMPORTANT ================================= # XXX: consider providing an interface here... class AbstractDataSetWidget(object): """ Base class for 'widgets' handled by `DataSetEditLayout` and it's derived classes. This is a generic representation of an input (or display) widget that has a label and one or more entry field. `DataSetEditLayout` uses a registry of *Item* to *Widget* mapping in order to automatically create a GUI for a `DataSet` structure """ READ_ONLY = False def __init__(self, item, parent_layout): """Derived constructors should create the necessary widgets The base class keeps a reference to item and parent """ self.item = item self.parent_layout = parent_layout self.group = None # Layout/Widget grouping items self.label = None self.build_mode = False def place_label(self, layout, row, column): """ Place item label on layout at specified position (row, column) """ label_text = self.item.get_prop_value("display", "label") unit = self.item.get_prop_value("display", "unit", '') if unit and not self.READ_ONLY: label_text += (' (%s)' % unit) self.label = QLabel(label_text) self.label.setToolTip(self.item.get_help()) layout.addWidget(self.label, row, column) def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """ Place widget on layout at specified position """ self.place_label(layout, row, label_column) layout.addWidget(self.group, row, widget_column, row_span, column_span) def is_active(self): """ Return True if associated item is active """ return self.item.get_prop_value("display", "active", True) def check(self): """ Item validator """ return True def set(self): """ Update data item value from widget contents """ # XXX: consider using item.set instead of item.set_from_string... self.item.set_from_string(self.value()) def get(self): """ Update widget contents from data item value """ pass def value(self): """ Returns the widget's current value """ return None def set_state(self): """ Update the visual status of the widget """ active = self.is_active() if self.group: self.group.setEnabled(active) if self.label: self.label.setEnabled(active) class GroupWidget(AbstractDataSetWidget): """ GroupItem widget """ def __init__(self, item, parent_layout): super(GroupWidget, self).__init__(item, parent_layout) embedded = item.get_prop_value("display", "embedded", False) if not embedded: self.group = QGroupBox(item.get_prop_value("display", "label")) else: self.group = QFrame() self.layout = QGridLayout() EditLayoutClass = parent_layout.__class__ self.edit = EditLayoutClass(self.group, item.instance, self.layout, item.item.group) self.group.setLayout(self.layout) def get(self): """Override AbstractDataSetWidget method""" self.edit.update_widgets() def set(self): """Override AbstractDataSetWidget method""" self.edit.accept_changes() def check(self): """Override AbstractDataSetWidget method""" return self.edit.check_all_values() def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" layout.addWidget(self.group, row, label_column, row_span, column_span+1) class TabGroupWidget(AbstractDataSetWidget): def __init__(self, item, parent_layout): super(TabGroupWidget, self).__init__(item, parent_layout) self.tabs = QTabWidget() items = item.item.group self.widgets = [] for item in items: if item.get_prop_value("display", parent_layout.instance, "hide", False): continue item.set_prop("display", embedded=True) widget = parent_layout.build_widget(item) frame = QFrame() label = widget.item.get_prop_value("display", "label") icon = widget.item.get_prop_value("display", "icon", None) if icon is not None: self.tabs.addTab(frame, get_icon(icon), label) else: self.tabs.addTab(frame, label) layout = QGridLayout() layout.setAlignment(Qt.AlignTop) frame.setLayout(layout) widget.place_on_grid(layout, 0, 0, 1) try: widget.get() except Exception: print("Error building item :", item.item._name) raise self.widgets.append(widget) def get(self): """Override AbstractDataSetWidget method""" for widget in self.widgets: widget.get() def set(self): """Override AbstractDataSetWidget method""" for widget in self.widgets: widget.set() def check(self): """Override AbstractDataSetWidget method""" return True def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" layout.addWidget(self.tabs, row, label_column, row_span, column_span+1) class LineEditWidget(AbstractDataSetWidget): """ QLineEdit-based widget """ def __init__(self, item, parent_layout): super(LineEditWidget, self).__init__(item, parent_layout) self.edit = self.group = QLineEdit() self.edit.setToolTip(item.get_help()) if hasattr(item, "min_equals_max") and item.min_equals_max(): if item.check_item(): self.edit.setEnabled(False) self.edit.setToolTip(_("Value is forced to %d") % item.get_max()) self.edit.textChanged.connect(self.line_edit_changed) def get(self): """Override AbstractDataSetWidget method""" value = self.item.get() old_value = to_text_string(self.value()) if value is not None: if isinstance(value, QColor): # if item is a ColorItem object value = value.name() uvalue = utf8_to_unicode(value) if uvalue != old_value: self.edit.setText(utf8_to_unicode(value)) else: self.line_edit_changed(value) def line_edit_changed(self, qvalue): """QLineEdit validator""" if qvalue is not None: value = self.item.from_string(to_text_string(qvalue)) else: value = None if not self.item.check_value(value): self.edit.setStyleSheet("background-color:rgb(255, 175, 90);") else: self.edit.setStyleSheet("") cb = self.item.get_prop_value("display", "callback", None) if cb is not None: if self.build_mode: self.set() else: self.parent_layout.update_dataitems() cb(self.item.instance, self.item.item, value) self.parent_layout.update_widgets(except_this_one=self) self.update(value) def update(self, value): """Override AbstractDataSetWidget method""" cb = self.item.get_prop_value("display", "value_callback", None) if cb is not None: cb(value) def value(self): return to_text_string(self.edit.text()) def check(self): """Override AbstractDataSetWidget method""" value = self.item.from_string(to_text_string(self.edit.text())) return self.item.check_value(value) class TextEditWidget(AbstractDataSetWidget): """ QTextEdit-based widget """ def __init__(self, item, parent_layout): super(TextEditWidget, self).__init__(item, parent_layout) self.edit = self.group = QTextEdit() self.edit.setToolTip(item.get_help()) if hasattr(item, "min_equals_max") and item.min_equals_max(): if item.check_item(): self.edit.setEnabled(False) self.edit.setToolTip(_("Value is forced to %d") % item.get_max()) self.edit.textChanged.connect(self.text_changed) def __get_text(self): """Get QTextEdit text, replacing UTF-8 EOL chars by os.linesep""" return to_text_string(self.edit.toPlainText()).replace('\u2029', os.linesep) def get(self): """Override AbstractDataSetWidget method""" value = self.item.get() if value is not None: self.edit.setPlainText(utf8_to_unicode(value)) self.text_changed() def text_changed(self): """QLineEdit validator""" value = self.item.from_string(self.__get_text()) if not self.item.check_value(value): self.edit.setStyleSheet("background-color:rgb(255, 175, 90);") else: self.edit.setStyleSheet("") self.update(value) def update(self, value): """Override AbstractDataSetWidget method""" pass def value(self): return self.edit.toPlainText() def check(self): """Override AbstractDataSetWidget method""" value = self.item.from_string(self.__get_text()) return self.item.check_value(value) class CheckBoxWidget(AbstractDataSetWidget): """ BoolItem widget """ def __init__(self, item, parent_layout): super(CheckBoxWidget, self).__init__(item, parent_layout) self.checkbox = QCheckBox(self.item.get_prop_value("display", "text")) self.checkbox.setToolTip(item.get_help()) self.group = self.checkbox self.store = self.item.get_prop("display", "store", None) if self.store: self.checkbox.stateChanged.connect(self.do_store) def get(self): """Override AbstractDataSetWidget method""" value = self.item.get() if value is not None: self.checkbox.setChecked(value) def set(self): """Override AbstractDataSetWidget method""" self.item.set(self.value()) def value(self): return self.checkbox.isChecked() def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" if not self.item.get_prop_value("display", "label"): widget_column = label_column column_span += 1 else: self.place_label(layout, row, label_column) layout.addWidget(self.group, row, widget_column, row_span, column_span) def do_store(self, state): self.store.set(self.item.instance, self.item.item, state) self.parent_layout.refresh_widgets() class DateWidget(AbstractDataSetWidget): """ DateItem widget """ def __init__(self, item, parent_layout): super(DateWidget, self).__init__(item, parent_layout) self.dateedit = self.group = QDateEdit() self.dateedit.setToolTip(item.get_help()) def get(self): """Override AbstractDataSetWidget method""" value = self.item.get() if value: if not isinstance(value, datetime.date): value = datetime.date.fromordinal(value) self.dateedit.setDate(value) def set(self): """Override AbstractDataSetWidget method""" self.item.set(self.value()) def value(self): return self.dateedit.date().toPyDate() class DateTimeWidget(AbstractDataSetWidget): """ DateTimeItem widget """ def __init__(self, item, parent_layout): super(DateTimeWidget, self).__init__(item, parent_layout) self.dateedit = self.group = QDateTimeEdit() self.dateedit.setCalendarPopup(True) self.dateedit.setToolTip(item.get_help()) def get(self): """Override AbstractDataSetWidget method""" value = self.item.get() if value: if not isinstance(value, datetime.datetime): value = datetime.datetime.fromtimestamp(value) self.dateedit.setDateTime(value) def set(self): """Override AbstractDataSetWidget method""" self.item.set(self.value()) def value(self): return self.dateedit.dateTime().toPyDateTime() class GroupLayout(QHBoxLayout): def __init__(self): QHBoxLayout.__init__(self) self.widgets = [] def addWidget(self, widget): QHBoxLayout.addWidget(self, widget) self.widgets.append(widget) def setEnabled(self, state): for widget in self.widgets: widget.setEnabled(state) class HLayoutMixin(object): def __init__(self, item, parent_layout): super(HLayoutMixin, self).__init__(item, parent_layout) old_group = self.group self.group = GroupLayout() self.group.addWidget(old_group) def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" self.place_label(layout, row, label_column) layout.addLayout(self.group, row, widget_column, row_span, column_span) class ColorWidget(HLayoutMixin, LineEditWidget): """ ColorItem widget """ def __init__(self, item, parent_layout): super(ColorWidget, self).__init__(item, parent_layout) self.button = QPushButton("") self.button.setMaximumWidth(32) self.button.clicked.connect(self.select_color) self.group.addWidget(self.button) def update(self, value): """Reimplement LineEditWidget method""" LineEditWidget.update(self, value) color = text_to_qcolor(value) if color.isValid(): bitmap = QPixmap(16, 16) bitmap.fill(color) icon = QIcon(bitmap) else: icon = get_icon("not_found") self.button.setIcon(icon) def select_color(self): """Open a color selection dialog box""" color = text_to_qcolor(self.edit.text()) if not color.isValid(): color = Qt.gray color = QColorDialog.getColor(color, self.parent_layout.parent) if color.isValid(): value = color.name() self.edit.setText(value) self.update(value) class SliderWidget(HLayoutMixin, LineEditWidget): """ IntItem with Slider """ DATA_TYPE = int def __init__(self, item, parent_layout): super(SliderWidget, self).__init__(item, parent_layout) self.slider = self.vmin = self.vmax = None if item.get_prop_value("display", "slider"): self.vmin = item.get_prop_value("data", "min") self.vmax = item.get_prop_value("data", "max") assert self.vmin is not None and self.vmax is not None, \ "SliderWidget requires that item min/max have been defined" self.slider = QSlider() self.slider.setOrientation(Qt.Horizontal) self.setup_slider(item) self.slider.valueChanged.connect(self.value_changed) self.group.addWidget(self.slider) def value_to_slider(self, value): return value def slider_to_value(self, value): return value def setup_slider(self, item): self.slider.setRange(self.vmin, self.vmax) def update(self, value): """Reimplement LineEditWidget method""" LineEditWidget.update(self, value) if self.slider is not None and isinstance(value, self.DATA_TYPE): self.slider.blockSignals(True) self.slider.setValue(self.value_to_slider(value)) self.slider.blockSignals(False) def value_changed(self, ivalue): """Update the lineedit""" value = str(self.slider_to_value(ivalue)) self.edit.setText(value) self.update(value) class FloatSliderWidget(SliderWidget): """ FloatItem with Slider """ DATA_TYPE = float def value_to_slider(self, value): return (value-self.vmin)*100/(self.vmax-self.vmin) def slider_to_value(self, value): return value*(self.vmax-self.vmin)/100+self.vmin def setup_slider(self, item): self.slider.setRange(0, 100) def _get_child_title_func(ancestor): previous_ancestor = None while True: try: if previous_ancestor is ancestor: break return ancestor.child_title except AttributeError: previous_ancestor = ancestor ancestor = ancestor.parent() return lambda item: '' class FileWidget(HLayoutMixin, LineEditWidget): """ File path item widget """ def __init__(self, item, parent_layout, filedialog): super(FileWidget, self).__init__(item, parent_layout) self.filedialog = filedialog button = QPushButton() fmt = item.get_prop_value("data", "formats") button.setIcon(get_icon('%s.png' % fmt[0].lower(), default='file.png')) button.clicked.connect(self.select_file) self.group.addWidget(button) self.basedir = item.get_prop_value("data", "basedir") self.all_files_first = item.get_prop_value("data", "all_files_first") def select_file(self): """Open a file selection dialog box""" fname = self.item.from_string(to_text_string(self.edit.text())) if isinstance(fname, list): fname = osp.dirname(fname[0]) parent = self.parent_layout.parent _temp = sys.stdout sys.stdout = None if len(fname) == 0: fname = self.basedir _formats = self.item.get_prop_value("data", "formats") formats = [to_text_string(format).lower() for format in _formats] filter_lines = [(_("%s files")+" (*.%s)") % (format.upper(), format) for format in formats] all_filter = _("All supported files")+" (*.%s)" % " *.".join(formats) if len(formats) > 1: if self.all_files_first: filter_lines.insert(0, all_filter) else: filter_lines.append(all_filter) if fname is None: fname = "" child_title = _get_child_title_func(parent) fname, _filter = self.filedialog(parent, child_title(self.item), fname, "\n".join(filter_lines)) sys.stdout = _temp if fname: if isinstance(fname, list): fname = to_text_string(fname) self.edit.setText(fname) class DirectoryWidget(HLayoutMixin, LineEditWidget): """ Directory path item widget """ def __init__(self, item, parent_layout): super(DirectoryWidget, self).__init__(item, parent_layout) button = QPushButton() button.setIcon(get_std_icon('DirOpenIcon')) button.clicked.connect(self.select_directory) self.group.addWidget(button) def select_directory(self): """Open a directory selection dialog box""" value = self.item.from_string(to_text_string(self.edit.text())) parent = self.parent_layout.parent child_title = _get_child_title_func(parent) dname = getexistingdirectory(parent, child_title(self.item), value) if dname: self.edit.setText(dname) class ChoiceWidget(AbstractDataSetWidget): """ Choice item widget """ def __init__(self, item, parent_layout): super(ChoiceWidget, self).__init__(item, parent_layout) self._first_call = True self.is_radio = item.get_prop_value("display", "radio") self.store = self.item.get_prop("display", "store", None) if self.is_radio: self.group = QGroupBox() self.group.setToolTip(item.get_help()) self.vbox = QVBoxLayout() self.group.setLayout(self.vbox) self._buttons = [] else: self.combobox = self.group = QComboBox() self.combobox.setToolTip(item.get_help()) self.combobox.currentIndexChanged.connect(self.index_changed) def index_changed(self, index): if self.store: self.store.set(self.item.instance, self.item.item, self.value()) self.parent_layout.refresh_widgets() cb = self.item.get_prop_value("display", "callback", None) if cb is not None: if self.build_mode: self.set() else: self.parent_layout.update_dataitems() cb(self.item.instance, self.item.item, self.value()) self.parent_layout.update_widgets(except_this_one=self) def initialize_widget(self): if self.is_radio: for button in self._buttons: button.toggled.disconnect(self.index_changed) self.vbox.removeWidget(button) button.deleteLater() self._buttons = [] else: self.combobox.blockSignals(True) while self.combobox.count(): self.combobox.removeItem(0) _choices = self.item.get_prop_value("data", "choices") for key, lbl, img in _choices: if self.is_radio: button = QRadioButton(lbl, self.group) if img: if is_text_string(img): if not osp.isfile(img): img = get_image_file_path(img) img = QIcon(img) elif isinstance(img, collections.Callable): img = img(key) if self.is_radio: button.setIcon(img) else: self.combobox.addItem(img, lbl) elif not self.is_radio: self.combobox.addItem(lbl) if self.is_radio: self._buttons.append(button) self.vbox.addWidget(button) button.toggled.connect(self.index_changed) if not self.is_radio: self.combobox.blockSignals(False) def set_widget_value(self, idx): if self.is_radio: for button in self._buttons: button.blockSignals(True) self._buttons[idx].setChecked(True) for button in self._buttons: button.blockSignals(False) else: self.combobox.blockSignals(True) self.combobox.setCurrentIndex(idx) self.combobox.blockSignals(False) def get_widget_value(self): if self.is_radio: for index, widget in enumerate(self._buttons): if widget.isChecked(): return index else: return self.combobox.currentIndex() def get(self): """Override AbstractDataSetWidget method""" self.initialize_widget() value = self.item.get() if value is not None: idx = 0 _choices = self.item.get_prop_value("data", "choices") for key, _val, _img in _choices: if key == value: break idx += 1 self.set_widget_value(idx) if self._first_call: self.index_changed(idx) self._first_call = False def set(self): """Override AbstractDataSetWidget method""" try: value = self.value() except IndexError: return self.item.set(value) def value(self): index = self.get_widget_value() choices = self.item.get_prop_value("data", "choices") return choices[index][0] class MultipleChoiceWidget(AbstractDataSetWidget): """ Multiple choice item widget """ def __init__(self, item, parent_layout): super(MultipleChoiceWidget, self).__init__(item, parent_layout) self.groupbox = self.group = QGroupBox(item.get_prop_value("display", "label")) layout = QGridLayout() self.boxes = [] nx, ny = item.get_prop_value("display", "shape") cx, cy = 0, 0 _choices = item.get_prop_value("data", "choices") for _, choice, _img in _choices: checkbox = QCheckBox(choice) layout.addWidget(checkbox, cx, cy) if nx < 0: cy += 1 if cy >= ny: cy = 0 cx += 1 else: cx += 1 if cx >= nx: cx = 0 cy += 1 self.boxes.append(checkbox) self.groupbox.setLayout(layout) def get(self): """Override AbstractDataSetWidget method""" value = self.item.get() _choices = self.item.get_prop_value("data", "choices") for (i, _choice, _img), checkbox in zip(_choices, self.boxes): if value is not None and i in value: checkbox.setChecked(True) def set(self): """Override AbstractDataSetWidget method""" _choices = self.item.get_prop_value("data", "choices") choices = [ _choices[i][0] for i in self.value() ] self.item.set(choices) def value(self): return [ i for i, w in enumerate(self.boxes) if w.isChecked()] def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" layout.addWidget(self.group, row, label_column, row_span, column_span+1) class FloatArrayWidget(AbstractDataSetWidget): """ FloatArrayItem widget """ def __init__(self, item, parent_layout): super(FloatArrayWidget, self).__init__(item, parent_layout) _label = item.get_prop_value("display", "label") self.groupbox = self.group = QGroupBox(_label) self.layout = QGridLayout() self.layout.setAlignment(Qt.AlignLeft) self.groupbox.setLayout(self.layout) self.first_line, self.dim_label = get_image_layout("shape.png", _("Number of rows x Number of columns")) edit_button = QPushButton(get_icon("arredit.png"), "") edit_button.setToolTip(_("Edit array contents")) edit_button.setMaximumWidth(32) self.first_line.addWidget(edit_button) self.layout.addLayout(self.first_line, 0, 0) self.min_line, self.min_label = get_image_layout("min.png", _("Smallest element in array")) self.layout.addLayout(self.min_line, 1, 0) self.max_line, self.max_label = get_image_layout("max.png", _("Largest element in array")) self.layout.addLayout(self.max_line, 2, 0) edit_button.clicked.connect(self.edit_array) self.arr = None # le tableau si il a été modifié self.instance = None def edit_array(self): """Open an array editor dialog""" parent = self.parent_layout.parent label = self.item.get_prop_value("display", "label") try: # Spyder 3 from spyderlib.widgets.variableexplorer import arrayeditor except ImportError: # Spyder 2 from spyderlib.widgets import arrayeditor editor = arrayeditor.ArrayEditor(parent) if editor.setup_and_check(self.arr, title=label): if editor.exec_(): self.update(self.arr) def get(self): """Override AbstractDataSetWidget method""" self.arr = numpy.array(self.item.get(), copy=False) if self.item.get_prop_value("display", "transpose"): self.arr = self.arr.T self.update(self.arr) def update(self, arr): """Override AbstractDataSetWidget method""" shape = arr.shape if len(shape) == 1: shape = (1,) + shape dim = " x ".join( [ str(d) for d in shape ]) self.dim_label.setText(dim) format = self.item.get_prop_value("display", "format") minmax = self.item.get_prop_value("display", "minmax") try: if minmax == "all": mint = format % arr.min() maxt = format % arr.max() elif minmax == "columns": mint = ", ".join([format % arr[r,:].min() for r in range(arr.shape[0])]) maxt = ", ".join([format % arr[r,:].max() for r in range(arr.shape[0])]) else: mint = ", ".join([format % arr[:, r].min() for r in range(arr.shape[1])]) maxt = ", ".join([format % arr[:, r].max() for r in range(arr.shape[1])]) except (TypeError, IndexError): mint, maxt = "-", "-" self.min_label.setText(mint) self.max_label.setText(maxt) def set(self): """Override AbstractDataSetWidget method""" if self.item.get_prop_value("display", "transpose"): value = self.value().T else: value = self.value() self.item.set(value) def value(self): return self.arr def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" layout.addWidget(self.group, row, label_column, row_span, column_span+1) class ButtonWidget(AbstractDataSetWidget): """ BoolItem widget """ def __init__(self, item, parent_layout): super(ButtonWidget, self).__init__(item, parent_layout) _label = self.item.get_prop_value("display", "label") self.button = self.group = QPushButton(_label) self.button.setToolTip(item.get_help()) _icon = self.item.get_prop_value("display", "icon") if _icon is not None: if is_text_string(_icon): _icon = get_icon(_icon) self.button.setIcon(_icon) self.button.clicked.connect(self.clicked) self.cb_value = None def get(self): """Override AbstractDataSetWidget method""" self.cb_value = self.item.get() def set(self): """Override AbstractDataSetWidget method""" self.item.set(self.value()) def value(self): return self.cb_value def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" layout.addWidget(self.group, row, label_column, row_span, column_span+1) def clicked(self, *args): self.parent_layout.update_dataitems() callback = self.item.get_prop_value("display", "callback") self.cb_value = callback(self.item.instance, self.item.item, self.cb_value, self.button.parent()) self.set() self.parent_layout.update_widgets() class DataSetWidget(AbstractDataSetWidget): """ DataSet widget """ def __init__(self, item, parent_layout): super(DataSetWidget, self).__init__(item, parent_layout) self.dataset = self.klass() # Création du layout contenant les champs d'édition du signal embedded = item.get_prop_value("display", "embedded", False) if not embedded: self.group = QGroupBox(item.get_prop_value("display", "label")) else: self.group = QFrame() self.layout = QGridLayout() self.group.setLayout(self.layout) EditLayoutClass = parent_layout.__class__ self.edit = EditLayoutClass(self.parent_layout.parent, self.dataset, self.layout) def get(self): """Override AbstractDataSetWidget method""" self.get_dataset() for widget in self.edit.widgets: widget.get() def set(self): """Override AbstractDataSetWidget method""" for widget in self.edit.widgets: widget.set() self.set_dataset() def get_dataset(self): """update's internal parameter representation from the item's stored value default behavior uses update_dataset and assumes internal dataset class is the same as item's value class""" item = self.item.get() update_dataset(self.dataset, item) def set_dataset(self): """update the item's value from the internal data representation default behavior uses restore_dataset and assumes internal dataset class is the same as item's value class""" item = self.item.get() restore_dataset(self.dataset, item) def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" layout.addWidget(self.group, row, label_column, row_span, column_span+1) guidata-1.7.4/guidata/dataset/dataitems.py0000666000000000000000000006530412612107374017246 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 CEA # Pierre Raybaut # Licensed under the terms of the CECILL License # (see guidata/__init__.py for details) """ dataset.dataitems ================= The ``guidata.dataset.dataitems`` module contains implementation for concrete DataItems. """ from __future__ import division, unicode_literals import os import re import datetime import collections from guidata.dataset.datatypes import DataItem, ItemProperty from guidata.utils import utf8_to_unicode, add_extension from guidata.config import _ from guidata.py3compat import to_text_string, is_text_string, TEXT_TYPES class NumericTypeItem(DataItem): """ Numeric data item """ type = None def __init__(self, label, default=None, min=None, max=None, nonzero=None, unit='', help='', check=True): DataItem.__init__(self, label, default=default, help=help) self.set_prop("data", min=min, max=max, nonzero=nonzero, check_value=check) self.set_prop("display", unit=unit) def get_auto_help(self, instance): """Override DataItem method""" auto_help = {int: _('integer'), float: _('float')}[self.type] _min = self.get_prop_value("data", instance, "min") _max = self.get_prop_value("data", instance, "max") nonzero = self.get_prop_value("data", instance, "nonzero") unit = self.get_prop_value("display", instance, "unit") if _min is not None and _max is not None: auto_help += _(" between ")+str(_min)+ _(" and ")+str(_max) elif _min is not None: auto_help += _(" higher than ")+str(_min) elif _max is not None: auto_help += _(" lower than ")+str(_max) if nonzero: auto_help += ", "+_("non zero") if unit: auto_help += (", %s %s" % (_("unit:"), unit)) return auto_help def format_string(self, instance, value, fmt, func): """Override DataItem method""" text = fmt % (func(value), ) # We add directly the unit to 'text' (instead of adding it # to 'fmt') to avoid string formatting error if '%' is in unit unit = self.get_prop_value("display", instance, "unit", '') if unit: text += ' '+unit return text def check_value(self, value): """Override DataItem method""" if not self.get_prop('data', 'check_value', True): return True if not isinstance(value, self.type): return False if self.get_prop("data", "nonzero") and value == 0: return False _min = self.get_prop("data", "min") if _min is not None: if value < _min: return False _max = self.get_prop("data", "max") if _max is not None: if value > _max: return False return True def from_string(self, value): """Override DataItem method""" value = to_text_string(value) # necessary if value is a QString # String may contains numerical operands: if re.match(r'^([\d\(\)\+/\-\*.]|e)+$', value): try: return self.type(eval(value)) except: pass return None class FloatItem(NumericTypeItem): """ Construct a float data item * label [string]: name * default [float]: default value (optional) * min [float]: minimum value (optional) * max [float]: maximum value (optional) * slider [bool]: if True, shows a slider widget right after the line edit widget (default is False) * step [float]: step between tick values with a slider widget (optional) * nonzero [bool]: if True, zero is not a valid value (optional) * unit [string]: physical unit (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ type = float def __init__(self, label, default=None, min=None, max=None, nonzero=None, unit='', step=0.1, slider=False, help='', check=True): super(FloatItem, self).__init__(label, default=default, min=min, max=max, nonzero=nonzero, unit=unit, help=help, check=check) self.set_prop("display", slider=slider) self.set_prop("data", step=step) def get_value_from_reader(self, reader): """Reads value from the reader object, inside the try...except statement defined in the base item `deserialize` method""" return reader.read_float() class IntItem(NumericTypeItem): """ Construct an integer data item * label [string]: name * default [int]: default value (optional) * min [int]: minimum value (optional) * max [int]: maximum value (optional) * nonzero [bool]: if True, zero is not a valid value (optional) * unit [string]: physical unit (optional) * even [bool]: if True, even values are valid, if False, odd values are valid if None (default), ignored (optional) * slider [bool]: if True, shows a slider widget right after the line edit widget (default is False) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ type = int def __init__(self, label, default=None, min=None, max=None, nonzero=None, unit='', even=None, slider=False, help='', check=True): super(IntItem, self).__init__(label, default=default, min=min, max=max, nonzero=nonzero, unit=unit, help=help, check=check) self.set_prop("data", even=even) self.set_prop("display", slider=slider) def get_auto_help(self, instance): """Override DataItem method""" auto_help = super(IntItem, self).get_auto_help(instance) even = self.get_prop_value("data", instance, "even") if even is not None: if even: auto_help += ", "+_("even") else: auto_help += ", "+_("odd") return auto_help def check_value(self, value): """Override DataItem method""" if not self.get_prop('data', 'check_value', True): return True valid = super(IntItem, self).check_value(value) if not valid: return False even = self.get_prop("data", "even") if even is not None: is_even = value//2 == value/2. if (even and not is_even) or (not even and is_even): return False return True def get_value_from_reader(self, reader): """Reads value from the reader object, inside the try...except statement defined in the base item `deserialize` method""" return reader.read_int() class StringItem(DataItem): """ Construct a string data item * label [string]: name * default [string]: default value (optional) * help [string]: text shown in tooltip (optional) * notempty [bool]: if True, empty string is not a valid value (opt.) * wordwrap [bool]: toggle word wrapping (optional) """ type = TEXT_TYPES def __init__(self, label, default=None, notempty=None, wordwrap=False, help=''): DataItem.__init__(self, label, default=default, help=help) self.set_prop("data", notempty=notempty) self.set_prop("display", wordwrap=wordwrap) def check_value(self, value): """Override DataItem method""" notempty = self.get_prop("data", "notempty") if notempty and not value: return False return True def from_string(self, value): """Override DataItem method""" # QString -> str return to_text_string(value) def get_value_from_reader(self, reader): """Reads value from the reader object, inside the try...except statement defined in the base item `deserialize` method""" return reader.read_unicode() class TextItem(StringItem): """ Construct a text data item (multiline string) * label [string]: name * default [string]: default value (optional) * help [string]: text shown in tooltip (optional) * notempty [bool]: if True, empty string is not a valid value (opt.) * wordwrap [bool]: toggle word wrapping (optional) """ def __init__(self, label, default=None, notempty=None, wordwrap=True, help=''): StringItem.__init__(self, label, default=default, notempty=notempty, wordwrap=wordwrap, help=help) class BoolItem(DataItem): """ Construct a boolean data item * text [string]: form's field name (optional) * label [string]: name * default [string]: default value (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ type = bool def __init__(self, text='', label='', default=None, help='', check=True): DataItem.__init__(self, label, default=default, help=help, check=check) self.set_prop("display", text=text) def get_value_from_reader(self, reader): """Reads value from the reader object, inside the try...except statement defined in the base item `deserialize` method""" return reader.read_bool() class DateItem(DataItem): """ Construct a date data item. * text [string]: form's field name (optional) * label [string]: name * default [datetime.date]: default value (optional) * help [string]: text shown in tooltip (optional) """ type = datetime.date class DateTimeItem(DateItem): pass class ColorItem(StringItem): """ Construct a color data item * label [string]: name * default [string]: default value (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) Color values are encoded as hexadecimal strings or Qt color names """ def check_value(self, value): """Override DataItem method""" if not self.get_prop('data', 'check_value', True): return True if not isinstance(value, self.type): return False from guidata.qthelpers import text_to_qcolor return text_to_qcolor(value).isValid() def get_value_from_reader(self, reader): """Reads value from the reader object, inside the try...except statement defined in the base item `deserialize` method""" # Using read_str converts `numpy.string_` to `str` -- otherwise, # when passing the string to a QColor Qt object, any numpy.string_ will # be interpreted as no color (black) return reader.read_str() class FileSaveItem(StringItem): """ Construct a path data item for a file to be saved * label [string]: name * formats [string (or string list)]: wildcard filter * default [string]: default value (optional) * basedir [string]: default base directory (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ def __init__(self, label, formats='*', default=None, basedir=None, all_files_first=False, help='', check=True): DataItem.__init__(self, label, default=default, help=help, check=check) if isinstance(formats, str): formats = [formats] self.set_prop("data", formats=formats) self.set_prop("data", basedir=basedir) self.set_prop("data", all_files_first=all_files_first) def get_auto_help(self, instance): """Override DataItem method""" formats = self.get_prop("data", "formats") return _("all file types") if formats == ['*'] \ else _("supported file types:") + " *.%s" % ", *.".join(formats) def check_value(self, value): """Override DataItem method""" if not self.get_prop('data', 'check_value', True): return True if not isinstance(value, self.type): return False return len(value)>0 def from_string(self, value): """Override DataItem method""" return add_extension(self, value) class FileOpenItem(FileSaveItem): """ Construct a path data item for a file to be opened * label [string]: name * formats [string (or string list)]: wildcard filter * default [string]: default value (optional) * basedir [string]: default base directory (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ def check_value(self, value): """Override DataItem method""" if not self.get_prop('data', 'check_value', True): return True if not isinstance(value, self.type): return False return os.path.exists(value) and os.path.isfile(value) class FilesOpenItem(FileSaveItem): """ Construct a path data item for multiple files to be opened. * label [string]: name * formats [string (or string list)]: wildcard filter * default [string]: default value (optional) * basedir [string]: default base directory (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ type = list def __init__(self, label, formats='*', default=None, basedir=None, all_files_first=False, help='', check=True): if is_text_string(default): default = [default] FileSaveItem.__init__(self, label, formats=formats, default=default, basedir=basedir, all_files_first=all_files_first, help=help, check=check) def check_value(self, value): """Override DataItem method""" if not self.get_prop('data', 'check_value', True): return True allexist = True for path in value: allexist = allexist and os.path.exists(path) \ and os.path.isfile(path) return allexist def from_string(self, value): """Override DataItem method""" value = to_text_string(value) if value.endswith("']") or value.endswith('"]'): value = eval(value) else: value = [value] return [add_extension(self, path) for path in value] def serialize(self, instance, writer): """Serialize this item""" value = self.get_value(instance) writer.write_sequence([fname.encode("utf-8") for fname in value]) def get_value_from_reader(self, reader): """Reads value from the reader object, inside the try...except statement defined in the base item `deserialize` method""" return [to_text_string(fname, "utf-8") for fname in reader.read_sequence()] class DirectoryItem(StringItem): """ Construct a path data item for a directory. * label [string]: name * default [string]: default value (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ def check_value(self, value): """Override DataItem method""" if not self.get_prop('data', 'check_value', True): return True if not isinstance(value, self.type): return False return os.path.exists(value) and os.path.isdir(value) class FirstChoice(object): pass class ChoiceItem(DataItem): """ Construct a data item for a list of choices. * label [string]: name * choices [list, tuple or function]: string list or (key, label) list function of two arguments (item, value) returning a list of tuples (key, label, image) where image is an icon path, a QIcon instance or a function of one argument (key) returning a QIcon instance * default [-]: default label or default key (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) * radio [bool]: if True, shows radio buttons instead of a combo box (default is False) """ def __init__(self, label, choices, default=FirstChoice, help='', check=True, radio=False): if isinstance(choices, collections.Callable): _choices_data = ItemProperty(choices) else: _choices_data = [] for idx, choice in enumerate(choices): _choices_data.append( self._normalize_choice(idx, choice) ) if default is FirstChoice and\ not isinstance(choices, collections.Callable): default = _choices_data[0][0] elif default is FirstChoice: default = None DataItem.__init__(self, label, default=default, help=help, check=check) self.set_prop("data", choices=_choices_data) self.set_prop("display", radio=radio) def _normalize_choice(self, idx, choice_tuple): if isinstance(choice_tuple, tuple): key, value = choice_tuple else: key = idx value = choice_tuple if isinstance(value, str): value = utf8_to_unicode(value) return (key, value, None) # def _choices(self, item): # _choices_data = self.get_prop("data", "choices") # if callable(_choices_data): # return _choices_data(self, item) # return _choices_data def get_string_value(self, instance): """Override DataItem method""" value = self.get_value(instance) choices = self.get_prop_value("data", instance, "choices") #print "ShowChoiceWidget:", choices, value for choice in choices: if choice[0] == value: return to_text_string(choice[1]) else: return DataItem.get_string_value(self, instance) class MultipleChoiceItem(ChoiceItem): """ Construct a data item for a list of choices -- multiple choices can be selected * label [string]: name * choices [list or tuple]: string list or (key, label) list * default [-]: default label or default key (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ def __init__(self, label, choices, default=(), help='', check=True): ChoiceItem.__init__(self, label, choices, default, help, check=check) self.set_prop("display", shape = (1, -1)) def horizontal(self, row_nb=1): """ Method to arange choice list horizontally on `n` rows Example: nb = MultipleChoiceItem("Number", ['1', '2', '3'] ).horizontal(2) """ self.set_prop("display", shape = (row_nb, -1)) return self def vertical(self, col_nb=1): """ Method to arange choice list vertically on `n` columns Example: nb = MultipleChoiceItem("Number", ['1', '2', '3'] ).vertical(2) """ self.set_prop("display", shape = (-1, col_nb)) return self def serialize(self, instance, writer): """Serialize this item""" value = self.get_value(instance) seq = [] _choices = self.get_prop_value("data", instance, "choices") for key, _label, _img in _choices: seq.append( key in value ) writer.write_sequence( seq ) def deserialize(self, instance, reader): """Deserialize this item""" flags = reader.read_sequence() # We could have trouble with objects providing their own choice # function which depend on not yet deserialized values _choices = self.get_prop_value("data", instance, "choices") value = [] for idx, flag in enumerate(flags): if flag: value.append( _choices[idx][0] ) self.__set__(instance, value) class ImageChoiceItem(ChoiceItem): """ Construct a data item for a list of choices with images * label [string]: name * choices [list, tuple or function]: (label, image) list or (key, label, image) list function of two arguments (item, value) returning a list of tuples (key, label, image) where image is an icon path, a QIcon instance or a function of one argument (key) returning a QIcon instance * default [-]: default label or default key (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ def _normalize_choice(self, idx, choice_tuple): assert isinstance(choice_tuple, tuple) if len(choice_tuple) == 3: key, value, img = choice_tuple else: key = idx value, img = choice_tuple if isinstance(value, str): value = utf8_to_unicode(value) return (key, value, img) class FloatArrayItem(DataItem): """ Construct a float array data item * label [string]: name * default [numpy.ndarray]: default value (optional) * help [string]: text shown in tooltip (optional) * format [string]: formatting string (example: '%.3f') (optional) * transpose [bool]: transpose matrix (display only) * large [bool]: view all float of the array * minmax [string]: "all" (default), "columns", "rows" * check [bool]: if False, value is not checked (optional, default=True) """ def __init__(self, label, default=None, help='', format='%.3f', transpose=False, minmax="all", check=True): DataItem.__init__(self, label, default=default, help=help, check=check) self.set_prop("display", format=format, transpose=transpose, minmax=minmax) def format_string(self, instance, value, fmt, func): """Override DataItem method""" larg = self.get_prop_value("display", instance, "large", False) fmt = self.get_prop_value("display", instance, "format", "%s") unit = self.get_prop_value("display", instance, "unit", "") v = func(value) if larg: text = "= [" for flt in v[:-1]: text += fmt % flt + "; " text += fmt % v[-1] + "]" else: text = "~= " + fmt % v.mean() text += " [" + fmt % v.min() text += " .. " + fmt % v.max() text += "]" text += " %s" % unit return to_text_string(text) def serialize(self, instance, writer): """Serialize this item""" value = self.get_value(instance) writer.write_array(value) def get_value_from_reader(self, reader): """Reads value from the reader object, inside the try...except statement defined in the base item `deserialize` method""" return reader.read_array() class ButtonItem(DataItem): """ Construct a simple button that calls a method when hit * label [string]: text shown on the button * callback [function]: function with four parameters (dataset, item, value, parent) - dataset [DataSet]: instance of the parent dataset - item [DataItem]: instance of ButtonItem (i.e. self) - value [unspecified]: value of ButtonItem (default ButtonItem value or last value returned by the callback) - parent [QObject]: button's parent widget * icon [QIcon or string]: icon show on the button (optional) (string: icon filename as in guidata/guiqwt image search paths) * default [unspecified]: default value passed to the callback (optional) * help [string]: text shown in button's tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) The value of this item is unspecified but is passed to the callback along with the whole dataset. The value is assigned the callback`s return value. """ def __init__(self, label, callback, icon=None, default=None, help='', check=True): DataItem.__init__(self, label, default=default, help=help, check=check) self.set_prop("display", callback=callback) self.set_prop("display", icon=icon) def serialize(self, instance, writer): pass def deserialize(self, instance, reader): pass class DictItem(ButtonItem): """ Construct a dictionary data item * label [string]: name * default [dict]: default value (optional) * help [string]: text shown in tooltip (optional) * check [bool]: if False, value is not checked (optional, default=True) """ def __init__(self, label, default=None, help='', check=True): def dictedit(instance, item, value, parent): try: # Spyder 3 from spyderlib.widgets.variableexplorer \ import collectionseditor Editor = collectionseditor.CollectionsEditor except ImportError: # Spyder 2 from spyderlib.widgets import dicteditor Editor = dicteditor.DictEditor editor = Editor(parent) value_was_none = value is None if value_was_none: value = {} editor.setup(value) if editor.exec_(): return editor.get_value() else: if value_was_none: return return value ButtonItem.__init__(self, label, dictedit, icon='dictedit.png', default=default, help=help, check=check) class FontFamilyItem(StringItem): """ Construct a font family name item * label [string]: name * default [string]: default value (optional) * help [string]: text shown in tooltip (optional) """ pass